{-# OPTIONS_HADDOCK prune #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE OverloadedStrings #-}

-- |
-- Module: Lightning.Protocol.BOLT3.Scripts
-- Copyright: (c) 2025 Jared Tobin
-- License: MIT
-- Maintainer: Jared Tobin <jared@ppad.tech>
--
-- Script templates for BOLT #3 transaction outputs.
--
-- Includes witness scripts for:
--
-- * Funding output (2-of-2 multisig)
-- * to_local output (revocable with CSV delay)
-- * to_remote output (P2WPKH or anchored)
-- * Anchor outputs
-- * Offered HTLC outputs
-- * Received HTLC outputs
-- * HTLC-timeout/success output (revocable with delay)

module Lightning.Protocol.BOLT3.Scripts (
    -- * Funding output
    funding_script
  , funding_witness

    -- * to_local output
  , to_local_script
  , to_local_witness_spend
  , to_local_witness_revoke

    -- * to_remote output
  , to_remote_script
  , to_remote_witness

    -- * Anchor outputs
  , anchor_script
  , anchor_witness_owner
  , anchor_witness_anyone

    -- * Offered HTLC output
  , offered_htlc_script
  , offered_htlc_witness_preimage
  , offered_htlc_witness_revoke

    -- * Received HTLC output
  , received_htlc_script
  , received_htlc_witness_timeout
  , received_htlc_witness_revoke

    -- * HTLC-timeout/success output (same as to_local)
  , htlc_output_script
  , htlc_output_witness_spend
  , htlc_output_witness_revoke

    -- * P2WSH helpers
  , to_p2wsh
  , witness_script_hash
  ) where

import Data.Bits ((.&.), shiftR)
import Data.Word (Word8, Word16, Word32)
import qualified Data.ByteString as BS
import qualified Data.ByteString.Builder as BSB
import qualified Data.ByteString.Lazy as BSL
import qualified Crypto.Hash.SHA256 as SHA256
import qualified Crypto.Hash.RIPEMD160 as RIPEMD160
import Lightning.Protocol.BOLT3.Types

-- opcodes ---------------------------------------------------------------------

-- | OP_0 / OP_FALSE (0x00)
op_0 :: Word8
op_0 :: Word8
op_0 = Word8
0x00

-- | OP_PUSHDATA for 1-75 bytes just uses the length as opcode
-- For data <=75 bytes, opcode is just the length

-- | OP_IF (0x63)
op_if :: Word8
op_if :: Word8
op_if = Word8
0x63

-- | OP_NOTIF (0x64)
op_notif :: Word8
op_notif :: Word8
op_notif = Word8
0x64

-- | OP_ELSE (0x67)
op_else :: Word8
op_else :: Word8
op_else = Word8
0x67

-- | OP_ENDIF (0x68)
op_endif :: Word8
op_endif :: Word8
op_endif = Word8
0x68

-- | OP_DROP (0x75)
op_drop :: Word8
op_drop :: Word8
op_drop = Word8
0x75

-- | OP_DUP (0x76)
op_dup :: Word8
op_dup :: Word8
op_dup = Word8
0x76

-- | OP_SWAP (0x7c)
op_swap :: Word8
op_swap :: Word8
op_swap = Word8
0x7c

-- | OP_SIZE (0x82)
op_size :: Word8
op_size :: Word8
op_size = Word8
0x82

-- | OP_EQUAL (0x87)
op_equal :: Word8
op_equal :: Word8
op_equal = Word8
0x87

-- | OP_EQUALVERIFY (0x88)
op_equalverify :: Word8
op_equalverify :: Word8
op_equalverify = Word8
0x88

-- | OP_IFDUP (0x73)
op_ifdup :: Word8
op_ifdup :: Word8
op_ifdup = Word8
0x73

-- | OP_HASH160 (0xa9)
op_hash160 :: Word8
op_hash160 :: Word8
op_hash160 = Word8
0xa9

-- | OP_CHECKSIG (0xac)
op_checksig :: Word8
op_checksig :: Word8
op_checksig = Word8
0xac

-- | OP_CHECKSIGVERIFY (0xad)
op_checksigverify :: Word8
op_checksigverify :: Word8
op_checksigverify = Word8
0xad

-- | OP_CHECKMULTISIG (0xae)
op_checkmultisig :: Word8
op_checkmultisig :: Word8
op_checkmultisig = Word8
0xae

-- | OP_CHECKLOCKTIMEVERIFY (0xb1)
op_checklocktimeverify :: Word8
op_checklocktimeverify :: Word8
op_checklocktimeverify = Word8
0xb1

-- | OP_CHECKSEQUENCEVERIFY (0xb2)
op_checksequenceverify :: Word8
op_checksequenceverify :: Word8
op_checksequenceverify = Word8
0xb2

-- | OP_1 (0x51)
op_1 :: Word8
op_1 :: Word8
op_1 = Word8
0x51

-- | OP_2 (0x52)
op_2 :: Word8
op_2 :: Word8
op_2 = Word8
0x52

-- | OP_16 (0x60)
op_16 :: Word8
op_16 :: Word8
op_16 = Word8
0x60

-- helpers ---------------------------------------------------------------------

-- | Push a bytestring onto the stack (handles length encoding).
--
-- For data <= 75 bytes, the length itself is the opcode.
push_data :: BS.ByteString -> BSB.Builder
push_data :: ByteString -> Builder
push_data !ByteString
bs
  | Int
len Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
75 = Word8 -> Builder
BSB.word8 (Int -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
len) Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> ByteString -> Builder
BSB.byteString ByteString
bs
  | Int
len Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
255 = Word8 -> Builder
BSB.word8 Word8
0x4c Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 (Int -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
len)
                 Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> ByteString -> Builder
BSB.byteString ByteString
bs
  | Int
len Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
65535 = Word8 -> Builder
BSB.word8 Word8
0x4d Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word16 -> Builder
BSB.word16LE (Int -> Word16
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
len)
                   Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> ByteString -> Builder
BSB.byteString ByteString
bs
  | Bool
otherwise = Word8 -> Builder
BSB.word8 Word8
0x4e Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word32 -> Builder
BSB.word32LE (Int -> Word32
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
len)
                Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> ByteString -> Builder
BSB.byteString ByteString
bs
  where
    !len :: Int
len = ByteString -> Int
BS.length ByteString
bs
{-# INLINE push_data #-}

-- | Encode a Word16 as minimal script number (for CSV delays).
push_csv_delay :: Word16 -> BSB.Builder
push_csv_delay :: Word16 -> Builder
push_csv_delay !Word16
n
  | Word16
n Word16 -> Word16 -> Bool
forall a. Eq a => a -> a -> Bool
== Word16
0 = Word8 -> Builder
BSB.word8 Word8
op_0
  | Word16
n Word16 -> Word16 -> Bool
forall a. Ord a => a -> a -> Bool
<= Word16
16 = Word8 -> Builder
BSB.word8 (Word8
0x50 Word8 -> Word8 -> Word8
forall a. Num a => a -> a -> a
+ Word16 -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word16
n)
  | Word16
n Word16 -> Word16 -> Bool
forall a. Ord a => a -> a -> Bool
<= Word16
0x7f = ByteString -> Builder
push_data (Word8 -> ByteString
BS.singleton (Word16 -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word16
n))
  | Word16
n Word16 -> Word16 -> Bool
forall a. Ord a => a -> a -> Bool
<= Word16
0x7fff = ByteString -> Builder
push_data ([Word8] -> ByteString
BS.pack [Word8
lo, Word8
hi])
  | Bool
otherwise = ByteString -> Builder
push_data ([Word8] -> ByteString
BS.pack [Word8
lo, Word8
hi, Word8
0x00])  -- need sign byte
  where
    !lo :: Word8
lo = Word16 -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Word16
n Word16 -> Word16 -> Word16
forall a. Bits a => a -> a -> a
.&. Word16
0xff)
    !hi :: Word8
hi = Word16 -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral ((Word16
n Word16 -> Int -> Word16
forall a. Bits a => a -> Int -> a
`shiftR` Int
8) Word16 -> Word16 -> Word16
forall a. Bits a => a -> a -> a
.&. Word16
0xff)
{-# INLINE push_csv_delay #-}

-- | Encode a Word32 as minimal script number (for CLTV).
push_cltv :: Word32 -> BSB.Builder
push_cltv :: Word32 -> Builder
push_cltv !Word32
n
  | Word32
n Word32 -> Word32 -> Bool
forall a. Eq a => a -> a -> Bool
== Word32
0 = Word8 -> Builder
BSB.word8 Word8
op_0
  | Word32
n Word32 -> Word32 -> Bool
forall a. Ord a => a -> a -> Bool
<= Word32
16 = Word8 -> Builder
BSB.word8 (Word8
0x50 Word8 -> Word8 -> Word8
forall a. Num a => a -> a -> a
+ Word32 -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word32
n)
  | Bool
otherwise = ByteString -> Builder
push_data (Word32 -> ByteString
encode_scriptnum Word32
n)
  where
    encode_scriptnum :: Word32 -> BS.ByteString
    encode_scriptnum :: Word32 -> ByteString
encode_scriptnum Word32
0 = ByteString
BS.empty
    encode_scriptnum !Word32
v =
      let -- Build bytes little-endian (LSB first)
          go :: Word32 -> [Word8]
          go :: Word32 -> [Word8]
go Word32
0 = []
          go !Word32
x = Word32 -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Word32
x Word32 -> Word32 -> Word32
forall a. Bits a => a -> a -> a
.&. Word32
0xff) Word8 -> [Word8] -> [Word8]
forall a. a -> [a] -> [a]
: Word32 -> [Word8]
go (Word32
x Word32 -> Int -> Word32
forall a. Bits a => a -> Int -> a
`shiftR` Int
8)
          !bytes :: [Word8]
bytes = Word32 -> [Word8]
go Word32
v
          -- If MSB has high bit set, need 0x00 suffix for positive numbers
          !result :: [Word8]
result = case [Word8] -> [Word8]
forall a. [a] -> [a]
reverse [Word8]
bytes of
            [] -> [Word8]
bytes
            (Word8
msb:[Word8]
_) | Word8
msb Word8 -> Word8 -> Word8
forall a. Bits a => a -> a -> a
.&. Word8
0x80 Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
/= Word8
0 -> [Word8]
bytes [Word8] -> [Word8] -> [Word8]
forall a. [a] -> [a] -> [a]
++ [Word8
0x00]
            [Word8]
_ -> [Word8]
bytes
      in [Word8] -> ByteString
BS.pack [Word8]
result
{-# INLINE push_cltv #-}

-- | Build script from builder.
build_script :: BSB.Builder -> Script
build_script :: Builder -> Script
build_script = ByteString -> Script
Script (ByteString -> Script)
-> (Builder -> ByteString) -> Builder -> Script
forall b c a. (b -> c) -> (a -> b) -> a -> c
. LazyByteString -> ByteString
BSL.toStrict (LazyByteString -> ByteString)
-> (Builder -> LazyByteString) -> Builder -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> LazyByteString
BSB.toLazyByteString
{-# INLINE build_script #-}

-- | HASH160 = RIPEMD160(SHA256(x))
hash160 :: BS.ByteString -> BS.ByteString
hash160 :: ByteString -> ByteString
hash160 = ByteString -> ByteString
RIPEMD160.hash (ByteString -> ByteString)
-> (ByteString -> ByteString) -> ByteString -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> ByteString
SHA256.hash
{-# INLINE hash160 #-}

-- P2WSH helpers ---------------------------------------------------------------

-- | Compute SHA256 hash of a witness script.
--
-- >>> witness_script_hash (Script "some_script")
-- <32-byte SHA256 hash>
witness_script_hash :: Script -> BS.ByteString
witness_script_hash :: Script -> ByteString
witness_script_hash (Script !ByteString
s) = ByteString -> ByteString
SHA256.hash ByteString
s
{-# INLINE witness_script_hash #-}

-- | Convert a witness script to P2WSH scriptPubKey.
--
-- P2WSH format: OP_0 <32-byte-hash>
--
-- >>> to_p2wsh some_witness_script
-- Script "\x00\x20<32-byte-hash>"
to_p2wsh :: Script -> Script
to_p2wsh :: Script -> Script
to_p2wsh !Script
script =
  let !h :: ByteString
h = Script -> ByteString
witness_script_hash Script
script
  in Builder -> Script
build_script (Word8 -> Builder
BSB.word8 Word8
op_0 Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> ByteString -> Builder
push_data ByteString
h)
{-# INLINE to_p2wsh #-}

-- funding output --------------------------------------------------------------

-- | Funding output witness script (2-of-2 multisig).
--
-- Script: @2 <pubkey1> <pubkey2> 2 OP_CHECKMULTISIG@
--
-- Where pubkey1 is lexicographically lesser.
--
-- >>> funding_script pk1 pk2
-- Script "R!<pk_lesser>!<pk_greater>R\xae"
funding_script :: FundingPubkey -> FundingPubkey -> Script
funding_script :: FundingPubkey -> FundingPubkey -> Script
funding_script (FundingPubkey (Pubkey !ByteString
pk1)) (FundingPubkey (Pubkey !ByteString
pk2)) =
  let (!ByteString
lesser, !ByteString
greater) = if ByteString
pk1 ByteString -> ByteString -> Bool
forall a. Ord a => a -> a -> Bool
<= ByteString
pk2 then (ByteString
pk1, ByteString
pk2) else (ByteString
pk2, ByteString
pk1)
  in Builder -> Script
build_script (Builder -> Script) -> Builder -> Script
forall a b. (a -> b) -> a -> b
$
       Word8 -> Builder
BSB.word8 Word8
op_2
       Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> ByteString -> Builder
push_data ByteString
lesser
       Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> ByteString -> Builder
push_data ByteString
greater
       Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_2
       Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_checkmultisig

-- | Witness for spending funding output.
--
-- Witness: @0 <sig1> <sig2>@
--
-- Signatures ordered to match pubkey order in script.
--
-- >>> funding_witness sig1 sig2
-- Witness ["", sig1, sig2]
funding_witness :: BS.ByteString -> BS.ByteString -> Witness
funding_witness :: ByteString -> ByteString -> Witness
funding_witness !ByteString
sig1 !ByteString
sig2 = [ByteString] -> Witness
Witness [ByteString
BS.empty, ByteString
sig1, ByteString
sig2]

-- to_local output -------------------------------------------------------------

-- | to_local witness script (revocable with CSV delay).
--
-- Script:
--
-- @
-- OP_IF
--     <revocationpubkey>
-- OP_ELSE
--     <to_self_delay>
--     OP_CHECKSEQUENCEVERIFY
--     OP_DROP
--     <local_delayedpubkey>
-- OP_ENDIF
-- OP_CHECKSIG
-- @
--
-- >>> to_local_script revpk delay localpk
-- Script "c!<revpk>g<delay>\xb2u!<localpk>h\xac"
to_local_script
  :: RevocationPubkey
  -> ToSelfDelay
  -> LocalDelayedPubkey
  -> Script
to_local_script :: RevocationPubkey -> ToSelfDelay -> LocalDelayedPubkey -> Script
to_local_script
  (RevocationPubkey (Pubkey !ByteString
revpk))
  (ToSelfDelay !Word16
delay)
  (LocalDelayedPubkey (Pubkey !ByteString
localpk)) =
    Builder -> Script
build_script (Builder -> Script) -> Builder -> Script
forall a b. (a -> b) -> a -> b
$
      Word8 -> Builder
BSB.word8 Word8
op_if
      Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> ByteString -> Builder
push_data ByteString
revpk
      Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_else
      Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word16 -> Builder
push_csv_delay Word16
delay
      Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_checksequenceverify
      Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_drop
      Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> ByteString -> Builder
push_data ByteString
localpk
      Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_endif
      Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_checksig

-- | Witness for delayed spend of to_local output.
--
-- Input nSequence must be set to to_self_delay.
--
-- Witness: @<local_delayedsig> <>@
--
-- >>> to_local_witness_spend sig
-- Witness [sig, ""]
to_local_witness_spend :: BS.ByteString -> Witness
to_local_witness_spend :: ByteString -> Witness
to_local_witness_spend !ByteString
sig = [ByteString] -> Witness
Witness [ByteString
sig, ByteString
BS.empty]

-- | Witness for revocation spend of to_local output.
--
-- Witness: @<revocation_sig> 1@
--
-- >>> to_local_witness_revoke sig
-- Witness [sig, "\x01"]
to_local_witness_revoke :: BS.ByteString -> Witness
to_local_witness_revoke :: ByteString -> Witness
to_local_witness_revoke !ByteString
sig = [ByteString] -> Witness
Witness [ByteString
sig, Word8 -> ByteString
BS.singleton Word8
0x01]

-- to_remote output ------------------------------------------------------------

-- | to_remote witness script.
--
-- With option_anchors:
--
-- @
-- <remotepubkey> OP_CHECKSIGVERIFY 1 OP_CHECKSEQUENCEVERIFY
-- @
--
-- Without option_anchors: P2WPKH (just the pubkey hash).
--
-- >>> to_remote_script pk (ChannelFeatures True)
-- Script "!<pk>\xadQ\xb2"
to_remote_script :: RemotePubkey -> ChannelFeatures -> Script
to_remote_script :: RemotePubkey -> ChannelFeatures -> Script
to_remote_script (RemotePubkey (Pubkey !ByteString
pk)) !ChannelFeatures
features
  | ChannelFeatures -> Bool
has_anchors ChannelFeatures
features =
      -- Anchors: script with 1-block CSV
      Builder -> Script
build_script (Builder -> Script) -> Builder -> Script
forall a b. (a -> b) -> a -> b
$
        ByteString -> Builder
push_data ByteString
pk
        Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_checksigverify
        Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_1
        Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_checksequenceverify
  | Bool
otherwise =
      -- No anchors: P2WPKH (OP_0 <20-byte-hash>)
      let !h :: ByteString
h = ByteString -> ByteString
hash160 ByteString
pk
      in Builder -> Script
build_script (Word8 -> Builder
BSB.word8 Word8
op_0 Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> ByteString -> Builder
push_data ByteString
h)

-- | Witness for spending to_remote output.
--
-- With option_anchors (P2WSH), input nSequence must be 1.
-- Witness: @<remote_sig>@ (witness script appended by caller)
--
-- Without option_anchors (P2WPKH):
-- Witness: @<remote_sig> <remotepubkey>@
--
-- >>> to_remote_witness sig pk (ChannelFeatures False)
-- Witness [sig, pk]
to_remote_witness :: BS.ByteString -> RemotePubkey -> ChannelFeatures -> Witness
to_remote_witness :: ByteString -> RemotePubkey -> ChannelFeatures -> Witness
to_remote_witness !ByteString
sig (RemotePubkey (Pubkey !ByteString
pk)) !ChannelFeatures
features
  | ChannelFeatures -> Bool
has_anchors ChannelFeatures
features = [ByteString] -> Witness
Witness [ByteString
sig]
  | Bool
otherwise = [ByteString] -> Witness
Witness [ByteString
sig, ByteString
pk]

-- anchor outputs --------------------------------------------------------------

-- | Anchor output witness script.
--
-- Script:
--
-- @
-- <funding_pubkey> OP_CHECKSIG OP_IFDUP
-- OP_NOTIF
--     OP_16 OP_CHECKSEQUENCEVERIFY
-- OP_ENDIF
-- @
--
-- >>> anchor_script fundpk
-- Script "!<fundpk>\xac\x73d`\xb2h"
anchor_script :: FundingPubkey -> Script
anchor_script :: FundingPubkey -> Script
anchor_script (FundingPubkey (Pubkey !ByteString
pk)) =
  Builder -> Script
build_script (Builder -> Script) -> Builder -> Script
forall a b. (a -> b) -> a -> b
$
    ByteString -> Builder
push_data ByteString
pk
    Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_checksig
    Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_ifdup
    Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_notif
    Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_16
    Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_checksequenceverify
    Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_endif

-- | Witness for owner to spend anchor output.
--
-- Witness: @<sig>@
--
-- >>> anchor_witness_owner sig
-- Witness [sig]
anchor_witness_owner :: BS.ByteString -> Witness
anchor_witness_owner :: ByteString -> Witness
anchor_witness_owner !ByteString
sig = [ByteString] -> Witness
Witness [ByteString
sig]

-- | Witness for anyone to sweep anchor output after 16 blocks.
--
-- Witness: @<>@
--
-- >>> anchor_witness_anyone
-- Witness [""]
anchor_witness_anyone :: Witness
anchor_witness_anyone :: Witness
anchor_witness_anyone = [ByteString] -> Witness
Witness [ByteString
BS.empty]

-- offered HTLC output ---------------------------------------------------------

-- | Offered HTLC witness script.
--
-- Without option_anchors:
--
-- @
-- OP_DUP OP_HASH160 <RIPEMD160(SHA256(revocationpubkey))> OP_EQUAL
-- OP_IF
--     OP_CHECKSIG
-- OP_ELSE
--     <remote_htlcpubkey> OP_SWAP OP_SIZE 32 OP_EQUAL
--     OP_NOTIF
--         OP_DROP 2 OP_SWAP <local_htlcpubkey> 2 OP_CHECKMULTISIG
--     OP_ELSE
--         OP_HASH160 <RIPEMD160(payment_hash)> OP_EQUALVERIFY
--         OP_CHECKSIG
--     OP_ENDIF
-- OP_ENDIF
-- @
--
-- With option_anchors, adds @1 OP_CHECKSEQUENCEVERIFY OP_DROP@ before
-- final OP_ENDIF.
offered_htlc_script
  :: RevocationPubkey
  -> RemoteHtlcPubkey
  -> LocalHtlcPubkey
  -> PaymentHash
  -> ChannelFeatures
  -> Script
offered_htlc_script :: RevocationPubkey
-> RemoteHtlcPubkey
-> LocalHtlcPubkey
-> PaymentHash
-> ChannelFeatures
-> Script
offered_htlc_script
  (RevocationPubkey (Pubkey !ByteString
revpk))
  (RemoteHtlcPubkey (Pubkey !ByteString
remotepk))
  (LocalHtlcPubkey (Pubkey !ByteString
localpk))
  (PaymentHash !ByteString
ph)
  !ChannelFeatures
features =
    let !revpk_hash :: ByteString
revpk_hash = ByteString -> ByteString
hash160 ByteString
revpk
        !payment_hash160 :: ByteString
payment_hash160 = ByteString -> ByteString
RIPEMD160.hash ByteString
ph
        !csv_suffix :: Builder
csv_suffix = if ChannelFeatures -> Bool
has_anchors ChannelFeatures
features
          then Word8 -> Builder
BSB.word8 Word8
op_1
               Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_checksequenceverify
               Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_drop
          else Builder
forall a. Monoid a => a
mempty
    in Builder -> Script
build_script (Builder -> Script) -> Builder -> Script
forall a b. (a -> b) -> a -> b
$
         -- OP_DUP OP_HASH160 <revpk_hash> OP_EQUAL
         Word8 -> Builder
BSB.word8 Word8
op_dup
         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_hash160
         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> ByteString -> Builder
push_data ByteString
revpk_hash
         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_equal
         -- OP_IF OP_CHECKSIG
         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_if
         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_checksig
         -- OP_ELSE
         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_else
         -- <remote_htlcpubkey> OP_SWAP OP_SIZE 32 OP_EQUAL
         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> ByteString -> Builder
push_data ByteString
remotepk
         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_swap
         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_size
         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> ByteString -> Builder
push_data (Word8 -> ByteString
BS.singleton Word8
32)
         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_equal
         -- OP_NOTIF
         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_notif
         -- OP_DROP 2 OP_SWAP <local_htlcpubkey> 2 OP_CHECKMULTISIG
         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_drop
         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_2
         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_swap
         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> ByteString -> Builder
push_data ByteString
localpk
         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_2
         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_checkmultisig
         -- OP_ELSE
         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_else
         -- OP_HASH160 <payment_hash160> OP_EQUALVERIFY OP_CHECKSIG
         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_hash160
         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> ByteString -> Builder
push_data ByteString
payment_hash160
         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_equalverify
         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_checksig
         -- OP_ENDIF
         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_endif
         -- CSV suffix for anchors
         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
csv_suffix
         -- OP_ENDIF
         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_endif

-- | Witness for remote node to claim offered HTLC with preimage.
--
-- With option_anchors, input nSequence must be 1.
--
-- Witness: @<remotehtlcsig> <payment_preimage>@
--
-- >>> offered_htlc_witness_preimage sig preimage
-- Witness [sig, preimage]
offered_htlc_witness_preimage
  :: BS.ByteString -> PaymentPreimage -> Witness
offered_htlc_witness_preimage :: ByteString -> PaymentPreimage -> Witness
offered_htlc_witness_preimage !ByteString
sig (PaymentPreimage !ByteString
preimage) =
  [ByteString] -> Witness
Witness [ByteString
sig, ByteString
preimage]

-- | Witness for revocation spend of offered HTLC.
--
-- Witness: @<revocation_sig> <revocationpubkey>@
--
-- >>> offered_htlc_witness_revoke sig revpk
-- Witness [sig, revpk]
offered_htlc_witness_revoke :: BS.ByteString -> Pubkey -> Witness
offered_htlc_witness_revoke :: ByteString -> Pubkey -> Witness
offered_htlc_witness_revoke !ByteString
sig (Pubkey !ByteString
revpk) = [ByteString] -> Witness
Witness [ByteString
sig, ByteString
revpk]

-- received HTLC output --------------------------------------------------------

-- | Received HTLC witness script.
--
-- Without option_anchors:
--
-- @
-- OP_DUP OP_HASH160 <RIPEMD160(SHA256(revocationpubkey))> OP_EQUAL
-- OP_IF
--     OP_CHECKSIG
-- OP_ELSE
--     <remote_htlcpubkey> OP_SWAP OP_SIZE 32 OP_EQUAL
--     OP_IF
--         OP_HASH160 <RIPEMD160(payment_hash)> OP_EQUALVERIFY
--         2 OP_SWAP <local_htlcpubkey> 2 OP_CHECKMULTISIG
--     OP_ELSE
--         OP_DROP <cltv_expiry> OP_CHECKLOCKTIMEVERIFY OP_DROP
--         OP_CHECKSIG
--     OP_ENDIF
-- OP_ENDIF
-- @
--
-- With option_anchors, adds @1 OP_CHECKSEQUENCEVERIFY OP_DROP@ before
-- final OP_ENDIF.
received_htlc_script
  :: RevocationPubkey
  -> RemoteHtlcPubkey
  -> LocalHtlcPubkey
  -> PaymentHash
  -> CltvExpiry
  -> ChannelFeatures
  -> Script
received_htlc_script :: RevocationPubkey
-> RemoteHtlcPubkey
-> LocalHtlcPubkey
-> PaymentHash
-> CltvExpiry
-> ChannelFeatures
-> Script
received_htlc_script
  (RevocationPubkey (Pubkey !ByteString
revpk))
  (RemoteHtlcPubkey (Pubkey !ByteString
remotepk))
  (LocalHtlcPubkey (Pubkey !ByteString
localpk))
  (PaymentHash !ByteString
ph)
  (CltvExpiry !Word32
expiry)
  !ChannelFeatures
features =
    let !revpk_hash :: ByteString
revpk_hash = ByteString -> ByteString
hash160 ByteString
revpk
        !payment_hash160 :: ByteString
payment_hash160 = ByteString -> ByteString
RIPEMD160.hash ByteString
ph
        !csv_suffix :: Builder
csv_suffix = if ChannelFeatures -> Bool
has_anchors ChannelFeatures
features
          then Word8 -> Builder
BSB.word8 Word8
op_1
               Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_checksequenceverify
               Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_drop
          else Builder
forall a. Monoid a => a
mempty
    in Builder -> Script
build_script (Builder -> Script) -> Builder -> Script
forall a b. (a -> b) -> a -> b
$
         -- OP_DUP OP_HASH160 <revpk_hash> OP_EQUAL
         Word8 -> Builder
BSB.word8 Word8
op_dup
         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_hash160
         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> ByteString -> Builder
push_data ByteString
revpk_hash
         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_equal
         -- OP_IF OP_CHECKSIG
         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_if
         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_checksig
         -- OP_ELSE
         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_else
         -- <remote_htlcpubkey> OP_SWAP OP_SIZE 32 OP_EQUAL
         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> ByteString -> Builder
push_data ByteString
remotepk
         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_swap
         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_size
         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> ByteString -> Builder
push_data (Word8 -> ByteString
BS.singleton Word8
32)
         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_equal
         -- OP_IF
         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_if
         -- OP_HASH160 <payment_hash160> OP_EQUALVERIFY
         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_hash160
         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> ByteString -> Builder
push_data ByteString
payment_hash160
         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_equalverify
         -- 2 OP_SWAP <local_htlcpubkey> 2 OP_CHECKMULTISIG
         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_2
         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_swap
         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> ByteString -> Builder
push_data ByteString
localpk
         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_2
         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_checkmultisig
         -- OP_ELSE
         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_else
         -- OP_DROP <cltv_expiry> OP_CHECKLOCKTIMEVERIFY OP_DROP OP_CHECKSIG
         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_drop
         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word32 -> Builder
push_cltv Word32
expiry
         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_checklocktimeverify
         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_drop
         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_checksig
         -- OP_ENDIF
         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_endif
         -- CSV suffix for anchors
         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
csv_suffix
         -- OP_ENDIF
         Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
BSB.word8 Word8
op_endif

-- | Witness for remote node to timeout received HTLC.
--
-- With option_anchors, input nSequence must be 1.
--
-- Witness: @<remotehtlcsig> <>@
--
-- >>> received_htlc_witness_timeout sig
-- Witness [sig, ""]
received_htlc_witness_timeout :: BS.ByteString -> Witness
received_htlc_witness_timeout :: ByteString -> Witness
received_htlc_witness_timeout !ByteString
sig = [ByteString] -> Witness
Witness [ByteString
sig, ByteString
BS.empty]

-- | Witness for revocation spend of received HTLC.
--
-- Witness: @<revocation_sig> <revocationpubkey>@
--
-- >>> received_htlc_witness_revoke sig revpk
-- Witness [sig, revpk]
received_htlc_witness_revoke :: BS.ByteString -> Pubkey -> Witness
received_htlc_witness_revoke :: ByteString -> Pubkey -> Witness
received_htlc_witness_revoke !ByteString
sig (Pubkey !ByteString
revpk) = [ByteString] -> Witness
Witness [ByteString
sig, ByteString
revpk]

-- HTLC-timeout/success output -------------------------------------------------

-- | HTLC output witness script (same structure as to_local).
--
-- Used for HTLC-timeout and HTLC-success transaction outputs.
--
-- Script:
--
-- @
-- OP_IF
--     <revocationpubkey>
-- OP_ELSE
--     <to_self_delay>
--     OP_CHECKSEQUENCEVERIFY
--     OP_DROP
--     <local_delayedpubkey>
-- OP_ENDIF
-- OP_CHECKSIG
-- @
htlc_output_script
  :: RevocationPubkey
  -> ToSelfDelay
  -> LocalDelayedPubkey
  -> Script
htlc_output_script :: RevocationPubkey -> ToSelfDelay -> LocalDelayedPubkey -> Script
htlc_output_script = RevocationPubkey -> ToSelfDelay -> LocalDelayedPubkey -> Script
to_local_script

-- | Witness for delayed spend of HTLC output.
--
-- Input nSequence must be set to to_self_delay.
--
-- Witness: @<local_delayedsig> 0@
htlc_output_witness_spend :: BS.ByteString -> Witness
htlc_output_witness_spend :: ByteString -> Witness
htlc_output_witness_spend = ByteString -> Witness
to_local_witness_spend

-- | Witness for revocation spend of HTLC output.
--
-- Witness: @<revocationsig> 1@
htlc_output_witness_revoke :: BS.ByteString -> Witness
htlc_output_witness_revoke :: ByteString -> Witness
htlc_output_witness_revoke = ByteString -> Witness
to_local_witness_revoke