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

-- |
-- Module: Lightning.Protocol.BOLT3.Encode
-- Copyright: (c) 2025 Jared Tobin
-- License: MIT
-- Maintainer: Jared Tobin <jared@ppad.tech>
--
-- Serialization for BOLT #3 transactions and scripts.
--
-- Provides Bitcoin transaction serialization in both standard SegWit
-- format (with witness data) and the stripped format used for signing.
--
-- == Transaction Format (SegWit)
--
-- * version (4 bytes LE)
-- * marker (0x00) + flag (0x01)
-- * input count (varint)
-- * inputs: outpoint (32+4), scriptSig length (varint), scriptSig, sequence
-- * output count (varint)
-- * outputs: value (8 LE), scriptPubKey length (varint), scriptPubKey
-- * witness data (for each input)
-- * locktime (4 bytes LE)

module Lightning.Protocol.BOLT3.Encode (
    -- * Transaction serialization
    encode_tx
  , encode_htlc_tx
  , encode_closing_tx
  , encode_tx_for_signing

    -- * Witness serialization
  , encode_witness
  , encode_funding_witness

    -- * Primitive encoding
  , encode_varint
  , encode_le32
  , encode_le64
  , encode_outpoint
  , encode_output
  ) where

import Data.Word (Word32, Word64)
import qualified Data.ByteString as BS
import qualified Data.ByteString.Builder as BSB
import qualified Data.ByteString.Lazy as BSL
import Lightning.Protocol.BOLT3.Types
import Lightning.Protocol.BOLT3.Tx

-- primitive encoding ----------------------------------------------------------

-- | Encode a 32-bit value in little-endian format.
--
-- >>> encode_le32 0x12345678
-- "\x78\x56\x34\x12"
encode_le32 :: Word32 -> BS.ByteString
encode_le32 :: Word32 -> ByteString
encode_le32 = LazyByteString -> ByteString
BSL.toStrict (LazyByteString -> ByteString)
-> (Word32 -> LazyByteString) -> Word32 -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> LazyByteString
BSB.toLazyByteString (Builder -> LazyByteString)
-> (Word32 -> Builder) -> Word32 -> LazyByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word32 -> Builder
BSB.word32LE
{-# INLINE encode_le32 #-}

-- | Encode a 64-bit value in little-endian format.
--
-- >>> encode_le64 0x123456789ABCDEF0
-- "\xF0\xDE\xBC\x9A\x78\x56\x34\x12"
encode_le64 :: Word64 -> BS.ByteString
encode_le64 :: Word64 -> ByteString
encode_le64 = LazyByteString -> ByteString
BSL.toStrict (LazyByteString -> ByteString)
-> (Word64 -> LazyByteString) -> Word64 -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> LazyByteString
BSB.toLazyByteString (Builder -> LazyByteString)
-> (Word64 -> Builder) -> Word64 -> LazyByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word64 -> Builder
BSB.word64LE
{-# INLINE encode_le64 #-}

-- | Encode a value as a Bitcoin varint (CompactSize).
--
-- Encoding scheme:
--
-- * 0-252: 1 byte
-- * 253-65535: 0xFD followed by 2 bytes LE
-- * 65536-4294967295: 0xFE followed by 4 bytes LE
-- * larger: 0xFF followed by 8 bytes LE
--
-- >>> encode_varint 100
-- "\x64"
-- >>> encode_varint 1000
-- "\xFD\xE8\x03"
encode_varint :: Word64 -> BS.ByteString
encode_varint :: Word64 -> ByteString
encode_varint !Word64
n
  | Word64
n Word64 -> Word64 -> Bool
forall a. Ord a => a -> a -> Bool
< Word64
0xFD = Word8 -> ByteString
BS.singleton (Word64 -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word64
n)
  | Word64
n Word64 -> Word64 -> Bool
forall a. Ord a => a -> a -> Bool
<= Word64
0xFFFF = LazyByteString -> ByteString
BSL.toStrict (LazyByteString -> ByteString) -> LazyByteString -> ByteString
forall a b. (a -> b) -> a -> b
$ Builder -> LazyByteString
BSB.toLazyByteString (Builder -> LazyByteString) -> Builder -> LazyByteString
forall a b. (a -> b) -> a -> b
$
      Word8 -> Builder
BSB.word8 Word8
0xFD Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word16 -> Builder
BSB.word16LE (Word64 -> Word16
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word64
n)
  | Word64
n Word64 -> Word64 -> Bool
forall a. Ord a => a -> a -> Bool
<= Word64
0xFFFFFFFF = LazyByteString -> ByteString
BSL.toStrict (LazyByteString -> ByteString) -> LazyByteString -> ByteString
forall a b. (a -> b) -> a -> b
$ Builder -> LazyByteString
BSB.toLazyByteString (Builder -> LazyByteString) -> Builder -> LazyByteString
forall a b. (a -> b) -> a -> b
$
      Word8 -> Builder
BSB.word8 Word8
0xFE Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word32 -> Builder
BSB.word32LE (Word64 -> Word32
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word64
n)
  | Bool
otherwise = LazyByteString -> ByteString
BSL.toStrict (LazyByteString -> ByteString) -> LazyByteString -> ByteString
forall a b. (a -> b) -> a -> b
$ Builder -> LazyByteString
BSB.toLazyByteString (Builder -> LazyByteString) -> Builder -> LazyByteString
forall a b. (a -> b) -> a -> b
$
      Word8 -> Builder
BSB.word8 Word8
0xFF Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word64 -> Builder
BSB.word64LE Word64
n
{-# INLINE encode_varint #-}

-- | Encode an outpoint (txid + output index).
--
-- Format: 32 bytes txid (already LE in TxId) + 4 bytes output index LE
--
-- >>> encode_outpoint (Outpoint txid 0)
-- <32-byte txid><4-byte index>
encode_outpoint :: Outpoint -> BS.ByteString
encode_outpoint :: Outpoint -> ByteString
encode_outpoint !Outpoint
op = LazyByteString -> ByteString
BSL.toStrict (LazyByteString -> ByteString) -> LazyByteString -> ByteString
forall a b. (a -> b) -> a -> b
$ Builder -> LazyByteString
BSB.toLazyByteString (Builder -> LazyByteString) -> Builder -> LazyByteString
forall a b. (a -> b) -> a -> b
$
  ByteString -> Builder
BSB.byteString (TxId -> ByteString
unTxId (TxId -> ByteString) -> TxId -> ByteString
forall a b. (a -> b) -> a -> b
$ Outpoint -> TxId
outpoint_txid Outpoint
op) Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<>
  Word32 -> Builder
BSB.word32LE (Outpoint -> Word32
outpoint_index Outpoint
op)
{-# INLINE encode_outpoint #-}

-- | Encode a transaction output.
--
-- Format: 8 bytes value LE + varint scriptPubKey length + scriptPubKey
--
-- >>> encode_output (TxOutput (Satoshi 100000) script OutputToLocal)
-- <8-byte value><varint length><scriptPubKey>
encode_output :: TxOutput -> BS.ByteString
encode_output :: TxOutput -> ByteString
encode_output !TxOutput
out = LazyByteString -> ByteString
BSL.toStrict (LazyByteString -> ByteString) -> LazyByteString -> ByteString
forall a b. (a -> b) -> a -> b
$ Builder -> LazyByteString
BSB.toLazyByteString (Builder -> LazyByteString) -> Builder -> LazyByteString
forall a b. (a -> b) -> a -> b
$
  let !script :: ByteString
script = Script -> ByteString
unScript (TxOutput -> Script
txout_script TxOutput
out)
      !scriptLen :: Word64
scriptLen = Int -> Word64
forall a b. (Integral a, Num b) => a -> b
fromIntegral (ByteString -> Int
BS.length ByteString
script) :: Word64
  in Word64 -> Builder
BSB.word64LE (Satoshi -> Word64
unSatoshi (Satoshi -> Word64) -> Satoshi -> Word64
forall a b. (a -> b) -> a -> b
$ TxOutput -> Satoshi
txout_value TxOutput
out) Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<>
     Word64 -> Builder
varint_builder Word64
scriptLen Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<>
     ByteString -> Builder
BSB.byteString ByteString
script
{-# INLINE encode_output #-}

-- witness encoding ------------------------------------------------------------

-- | Encode a witness stack.
--
-- Format: varint item count + (varint length + data) for each item
--
-- >>> encode_witness (Witness [sig, pubkey])
-- <varint 2><varint sigLen><sig><varint pkLen><pubkey>
encode_witness :: Witness -> BS.ByteString
encode_witness :: Witness -> ByteString
encode_witness (Witness ![ByteString]
items) = LazyByteString -> ByteString
BSL.toStrict (LazyByteString -> ByteString) -> LazyByteString -> ByteString
forall a b. (a -> b) -> a -> b
$ Builder -> LazyByteString
BSB.toLazyByteString (Builder -> LazyByteString) -> Builder -> LazyByteString
forall a b. (a -> b) -> a -> b
$
  let !count :: Word64
count = Int -> Word64
forall a b. (Integral a, Num b) => a -> b
fromIntegral ([ByteString] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [ByteString]
items) :: Word64
  in Word64 -> Builder
varint_builder Word64
count Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> [Builder] -> Builder
forall a. Monoid a => [a] -> a
mconcat ((ByteString -> Builder) -> [ByteString] -> [Builder]
forall a b. (a -> b) -> [a] -> [b]
map ByteString -> Builder
encode_witness_item [ByteString]
items)
{-# INLINE encode_witness #-}

-- | Encode a single witness stack item.
encode_witness_item :: BS.ByteString -> BSB.Builder
encode_witness_item :: ByteString -> Builder
encode_witness_item !ByteString
bs =
  let !len :: Word64
len = Int -> Word64
forall a b. (Integral a, Num b) => a -> b
fromIntegral (ByteString -> Int
BS.length ByteString
bs) :: Word64
  in Word64 -> Builder
varint_builder Word64
len Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> ByteString -> Builder
BSB.byteString ByteString
bs
{-# INLINE encode_witness_item #-}

-- | Encode a funding witness (2-of-2 multisig).
--
-- The witness stack is: @0 <sig1> <sig2> <witnessScript>@
--
-- Signatures must be ordered to match pubkey order in the funding script.
--
-- >>> encode_funding_witness sig1 sig2 fundingScript
-- <witness with 4 items: empty, sig1, sig2, script>
encode_funding_witness
  :: BS.ByteString  -- ^ Signature for pubkey1 (lexicographically lesser)
  -> BS.ByteString  -- ^ Signature for pubkey2 (lexicographically greater)
  -> Script         -- ^ The funding witness script
  -> BS.ByteString
encode_funding_witness :: ByteString -> ByteString -> Script -> ByteString
encode_funding_witness !ByteString
sig1 !ByteString
sig2 (Script !ByteString
witnessScript) =
  LazyByteString -> ByteString
BSL.toStrict (LazyByteString -> ByteString) -> LazyByteString -> ByteString
forall a b. (a -> b) -> a -> b
$ Builder -> LazyByteString
BSB.toLazyByteString (Builder -> LazyByteString) -> Builder -> LazyByteString
forall a b. (a -> b) -> a -> b
$
    Word64 -> Builder
varint_builder Word64
4 Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<>
    ByteString -> Builder
encode_witness_item ByteString
BS.empty Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<>
    ByteString -> Builder
encode_witness_item ByteString
sig1 Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<>
    ByteString -> Builder
encode_witness_item ByteString
sig2 Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<>
    ByteString -> Builder
encode_witness_item ByteString
witnessScript
{-# INLINE encode_funding_witness #-}

-- transaction encoding --------------------------------------------------------

-- | Encode a commitment transaction (SegWit format with witness).
--
-- SegWit format:
--
-- * version (4 bytes LE)
-- * marker (0x00)
-- * flag (0x01)
-- * input count (varint)
-- * inputs
-- * output count (varint)
-- * outputs
-- * witness data
-- * locktime (4 bytes LE)
--
-- Note: The witness is empty (just count=0) since the commitment tx
-- spending the funding output requires external signatures.
encode_tx :: CommitmentTx -> BS.ByteString
encode_tx :: CommitmentTx -> ByteString
encode_tx !CommitmentTx
tx = LazyByteString -> ByteString
BSL.toStrict (LazyByteString -> ByteString) -> LazyByteString -> ByteString
forall a b. (a -> b) -> a -> b
$ Builder -> LazyByteString
BSB.toLazyByteString (Builder -> LazyByteString) -> Builder -> LazyByteString
forall a b. (a -> b) -> a -> b
$
  -- Version
  Word32 -> Builder
BSB.word32LE (CommitmentTx -> Word32
ctx_version CommitmentTx
tx) Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<>
  -- SegWit marker and flag
  Word8 -> Builder
BSB.word8 Word8
0x00 Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<>
  Word8 -> Builder
BSB.word8 Word8
0x01 Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<>
  -- Input count (always 1 for commitment tx)
  Word64 -> Builder
varint_builder Word64
1 Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<>
  -- Input: outpoint + empty scriptSig + sequence
  ByteString -> Builder
BSB.byteString (Outpoint -> ByteString
encode_outpoint (CommitmentTx -> Outpoint
ctx_input_outpoint CommitmentTx
tx)) Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<>
  Word64 -> Builder
varint_builder Word64
0 Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<>  -- scriptSig length (empty for SegWit)
  Word32 -> Builder
BSB.word32LE (Sequence -> Word32
unSequence (Sequence -> Word32) -> Sequence -> Word32
forall a b. (a -> b) -> a -> b
$ CommitmentTx -> Sequence
ctx_input_sequence CommitmentTx
tx) Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<>
  -- Output count
  Word64 -> Builder
varint_builder (Int -> Word64
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> Word64) -> Int -> Word64
forall a b. (a -> b) -> a -> b
$ [TxOutput] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length ([TxOutput] -> Int) -> [TxOutput] -> Int
forall a b. (a -> b) -> a -> b
$ CommitmentTx -> [TxOutput]
ctx_outputs CommitmentTx
tx) Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<>
  -- Outputs
  [Builder] -> Builder
forall a. Monoid a => [a] -> a
mconcat ((TxOutput -> Builder) -> [TxOutput] -> [Builder]
forall a b. (a -> b) -> [a] -> [b]
map (ByteString -> Builder
BSB.byteString (ByteString -> Builder)
-> (TxOutput -> ByteString) -> TxOutput -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. TxOutput -> ByteString
encode_output) (CommitmentTx -> [TxOutput]
ctx_outputs CommitmentTx
tx)) Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<>
  -- Witness (empty stack for unsigned tx)
  Word64 -> Builder
varint_builder Word64
0 Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<>
  -- Locktime
  Word32 -> Builder
BSB.word32LE (Locktime -> Word32
unLocktime (Locktime -> Word32) -> Locktime -> Word32
forall a b. (a -> b) -> a -> b
$ CommitmentTx -> Locktime
ctx_locktime CommitmentTx
tx)

-- | Encode an HTLC transaction (SegWit format with witness).
--
-- HTLC transactions have a single input (the commitment tx HTLC output)
-- and a single output (the to_local-style delayed output).
encode_htlc_tx :: HTLCTx -> BS.ByteString
encode_htlc_tx :: HTLCTx -> ByteString
encode_htlc_tx !HTLCTx
tx = LazyByteString -> ByteString
BSL.toStrict (LazyByteString -> ByteString) -> LazyByteString -> ByteString
forall a b. (a -> b) -> a -> b
$ Builder -> LazyByteString
BSB.toLazyByteString (Builder -> LazyByteString) -> Builder -> LazyByteString
forall a b. (a -> b) -> a -> b
$
  -- Version
  Word32 -> Builder
BSB.word32LE (HTLCTx -> Word32
htx_version HTLCTx
tx) Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<>
  -- SegWit marker and flag
  Word8 -> Builder
BSB.word8 Word8
0x00 Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<>
  Word8 -> Builder
BSB.word8 Word8
0x01 Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<>
  -- Input count (always 1)
  Word64 -> Builder
varint_builder Word64
1 Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<>
  -- Input: outpoint + empty scriptSig + sequence
  ByteString -> Builder
BSB.byteString (Outpoint -> ByteString
encode_outpoint (HTLCTx -> Outpoint
htx_input_outpoint HTLCTx
tx)) Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<>
  Word64 -> Builder
varint_builder Word64
0 Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<>  -- scriptSig length (empty for SegWit)
  Word32 -> Builder
BSB.word32LE (Sequence -> Word32
unSequence (Sequence -> Word32) -> Sequence -> Word32
forall a b. (a -> b) -> a -> b
$ HTLCTx -> Sequence
htx_input_sequence HTLCTx
tx) Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<>
  -- Output count (always 1)
  Word64 -> Builder
varint_builder Word64
1 Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<>
  -- Output: value + scriptPubKey
  Word64 -> Builder
BSB.word64LE (Satoshi -> Word64
unSatoshi (Satoshi -> Word64) -> Satoshi -> Word64
forall a b. (a -> b) -> a -> b
$ HTLCTx -> Satoshi
htx_output_value HTLCTx
tx) Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<>
  let !script :: ByteString
script = Script -> ByteString
unScript (HTLCTx -> Script
htx_output_script HTLCTx
tx)
      !scriptLen :: Word64
scriptLen = Int -> Word64
forall a b. (Integral a, Num b) => a -> b
fromIntegral (ByteString -> Int
BS.length ByteString
script) :: Word64
  in Word64 -> Builder
varint_builder Word64
scriptLen Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> ByteString -> Builder
BSB.byteString ByteString
script Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<>
  -- Witness (empty stack for unsigned tx)
  Word64 -> Builder
varint_builder Word64
0 Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<>
  -- Locktime
  Word32 -> Builder
BSB.word32LE (Locktime -> Word32
unLocktime (Locktime -> Word32) -> Locktime -> Word32
forall a b. (a -> b) -> a -> b
$ HTLCTx -> Locktime
htx_locktime HTLCTx
tx)

-- | Encode a closing transaction (SegWit format with witness).
--
-- Closing transactions have a single input (the funding output) and
-- one or two outputs (to_local and/or to_remote).
encode_closing_tx :: ClosingTx -> BS.ByteString
encode_closing_tx :: ClosingTx -> ByteString
encode_closing_tx !ClosingTx
tx = LazyByteString -> ByteString
BSL.toStrict (LazyByteString -> ByteString) -> LazyByteString -> ByteString
forall a b. (a -> b) -> a -> b
$ Builder -> LazyByteString
BSB.toLazyByteString (Builder -> LazyByteString) -> Builder -> LazyByteString
forall a b. (a -> b) -> a -> b
$
  -- Version
  Word32 -> Builder
BSB.word32LE (ClosingTx -> Word32
cltx_version ClosingTx
tx) Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<>
  -- SegWit marker and flag
  Word8 -> Builder
BSB.word8 Word8
0x00 Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<>
  Word8 -> Builder
BSB.word8 Word8
0x01 Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<>
  -- Input count (always 1)
  Word64 -> Builder
varint_builder Word64
1 Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<>
  -- Input: outpoint + empty scriptSig + sequence
  ByteString -> Builder
BSB.byteString (Outpoint -> ByteString
encode_outpoint (ClosingTx -> Outpoint
cltx_input_outpoint ClosingTx
tx)) Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<>
  Word64 -> Builder
varint_builder Word64
0 Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<>  -- scriptSig length (empty for SegWit)
  Word32 -> Builder
BSB.word32LE (Sequence -> Word32
unSequence (Sequence -> Word32) -> Sequence -> Word32
forall a b. (a -> b) -> a -> b
$ ClosingTx -> Sequence
cltx_input_sequence ClosingTx
tx) Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<>
  -- Output count
  Word64 -> Builder
varint_builder (Int -> Word64
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> Word64) -> Int -> Word64
forall a b. (a -> b) -> a -> b
$ [TxOutput] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length ([TxOutput] -> Int) -> [TxOutput] -> Int
forall a b. (a -> b) -> a -> b
$ ClosingTx -> [TxOutput]
cltx_outputs ClosingTx
tx) Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<>
  -- Outputs
  [Builder] -> Builder
forall a. Monoid a => [a] -> a
mconcat ((TxOutput -> Builder) -> [TxOutput] -> [Builder]
forall a b. (a -> b) -> [a] -> [b]
map (ByteString -> Builder
BSB.byteString (ByteString -> Builder)
-> (TxOutput -> ByteString) -> TxOutput -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. TxOutput -> ByteString
encode_output) (ClosingTx -> [TxOutput]
cltx_outputs ClosingTx
tx)) Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<>
  -- Witness (empty stack for unsigned tx)
  Word64 -> Builder
varint_builder Word64
0 Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<>
  -- Locktime
  Word32 -> Builder
BSB.word32LE (Locktime -> Word32
unLocktime (Locktime -> Word32) -> Locktime -> Word32
forall a b. (a -> b) -> a -> b
$ ClosingTx -> Locktime
cltx_locktime ClosingTx
tx)

-- | Encode a commitment transaction for signing (stripped format).
--
-- The stripped format omits the SegWit marker, flag, and witness data.
-- This is the format used to compute the sighash for signing.
--
-- Format:
--
-- * version (4 bytes LE)
-- * input count (varint)
-- * inputs
-- * output count (varint)
-- * outputs
-- * locktime (4 bytes LE)
encode_tx_for_signing :: CommitmentTx -> BS.ByteString
encode_tx_for_signing :: CommitmentTx -> ByteString
encode_tx_for_signing !CommitmentTx
tx = LazyByteString -> ByteString
BSL.toStrict (LazyByteString -> ByteString) -> LazyByteString -> ByteString
forall a b. (a -> b) -> a -> b
$ Builder -> LazyByteString
BSB.toLazyByteString (Builder -> LazyByteString) -> Builder -> LazyByteString
forall a b. (a -> b) -> a -> b
$
  -- Version
  Word32 -> Builder
BSB.word32LE (CommitmentTx -> Word32
ctx_version CommitmentTx
tx) Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<>
  -- Input count (always 1 for commitment tx)
  Word64 -> Builder
varint_builder Word64
1 Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<>
  -- Input: outpoint + empty scriptSig + sequence
  ByteString -> Builder
BSB.byteString (Outpoint -> ByteString
encode_outpoint (CommitmentTx -> Outpoint
ctx_input_outpoint CommitmentTx
tx)) Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<>
  Word64 -> Builder
varint_builder Word64
0 Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<>  -- scriptSig length (empty for SegWit)
  Word32 -> Builder
BSB.word32LE (Sequence -> Word32
unSequence (Sequence -> Word32) -> Sequence -> Word32
forall a b. (a -> b) -> a -> b
$ CommitmentTx -> Sequence
ctx_input_sequence CommitmentTx
tx) Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<>
  -- Output count
  Word64 -> Builder
varint_builder (Int -> Word64
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> Word64) -> Int -> Word64
forall a b. (a -> b) -> a -> b
$ [TxOutput] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length ([TxOutput] -> Int) -> [TxOutput] -> Int
forall a b. (a -> b) -> a -> b
$ CommitmentTx -> [TxOutput]
ctx_outputs CommitmentTx
tx) Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<>
  -- Outputs
  [Builder] -> Builder
forall a. Monoid a => [a] -> a
mconcat ((TxOutput -> Builder) -> [TxOutput] -> [Builder]
forall a b. (a -> b) -> [a] -> [b]
map (ByteString -> Builder
BSB.byteString (ByteString -> Builder)
-> (TxOutput -> ByteString) -> TxOutput -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. TxOutput -> ByteString
encode_output) (CommitmentTx -> [TxOutput]
ctx_outputs CommitmentTx
tx)) Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<>
  -- Locktime
  Word32 -> Builder
BSB.word32LE (Locktime -> Word32
unLocktime (Locktime -> Word32) -> Locktime -> Word32
forall a b. (a -> b) -> a -> b
$ CommitmentTx -> Locktime
ctx_locktime CommitmentTx
tx)

-- internal helpers ------------------------------------------------------------

-- | Build a varint directly to Builder.
varint_builder :: Word64 -> BSB.Builder
varint_builder :: Word64 -> Builder
varint_builder !Word64
n
  | Word64
n Word64 -> Word64 -> Bool
forall a. Ord a => a -> a -> Bool
< Word64
0xFD = Word8 -> Builder
BSB.word8 (Word64 -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word64
n)
  | Word64
n Word64 -> Word64 -> Bool
forall a. Ord a => a -> a -> Bool
<= Word64
0xFFFF = Word8 -> Builder
BSB.word8 Word8
0xFD Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word16 -> Builder
BSB.word16LE (Word64 -> Word16
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word64
n)
  | Word64
n Word64 -> Word64 -> Bool
forall a. Ord a => a -> a -> Bool
<= Word64
0xFFFFFFFF = Word8 -> Builder
BSB.word8 Word8
0xFE Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word32 -> Builder
BSB.word32LE (Word64 -> Word32
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word64
n)
  | Bool
otherwise = Word8 -> Builder
BSB.word8 Word8
0xFF Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Word64 -> Builder
BSB.word64LE Word64
n
{-# INLINE varint_builder #-}