packages feed

ppad-bolt2-0.1.0: lib/Lightning/Protocol/BOLT2/Codec.hs

{-# OPTIONS_HADDOCK hide #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DeriveGeneric #-}

-- |
-- Module: Lightning.Protocol.BOLT2.Codec
-- Copyright: (c) 2025 Jared Tobin
-- License: MIT
-- Maintainer: Jared Tobin <jared@ppad.tech>
--
-- Encoding and decoding of BOLT #2 messages.

module Lightning.Protocol.BOLT2.Codec (
    EncodeError(..)
  , DecodeError(..)

  , encode_message
  , decode_message

  , encode_open_channel
  , decode_open_channel
  , encode_accept_channel
  , decode_accept_channel
  , encode_funding_created
  , decode_funding_created
  , encode_funding_signed
  , decode_funding_signed
  , encode_channel_ready
  , decode_channel_ready

  , encode_open_channel2
  , decode_open_channel2
  , encode_accept_channel2
  , decode_accept_channel2
  , encode_tx_add_input
  , decode_tx_add_input
  , encode_tx_add_output
  , decode_tx_add_output
  , encode_tx_remove_input
  , decode_tx_remove_input
  , encode_tx_remove_output
  , decode_tx_remove_output
  , encode_tx_complete
  , decode_tx_complete
  , encode_tx_signatures
  , decode_tx_signatures
  , encode_tx_init_rbf
  , decode_tx_init_rbf
  , encode_tx_ack_rbf
  , decode_tx_ack_rbf
  , encode_tx_abort
  , decode_tx_abort

  , encode_stfu
  , decode_stfu

  , encode_shutdown
  , decode_shutdown
  , encode_closing_complete
  , decode_closing_complete
  , encode_closing_sig
  , decode_closing_sig
  , encode_closing_signed
  , decode_closing_signed

  , encode_update_add_htlc
  , decode_update_add_htlc
  , encode_update_fulfill_htlc
  , decode_update_fulfill_htlc
  , encode_update_fail_htlc
  , decode_update_fail_htlc
  , encode_update_fail_malformed_htlc
  , decode_update_fail_malformed_htlc
  , encode_commitment_signed
  , decode_commitment_signed
  , encode_revoke_and_ack
  , decode_revoke_and_ack
  , encode_update_fee
  , decode_update_fee

  , encode_channel_reestablish
  , decode_channel_reestablish
  ) where

import Bitcoin.Prim.Tx (TxId, mk_txid, un_txid)
import Control.DeepSeq (NFData)
import qualified Data.ByteString as BS
import qualified Data.ByteString.Unsafe as BU
import Data.Int (Int64)
import Data.Word (Word8, Word16, Word32, Word64)
import GHC.Generics (Generic)
import qualified Lightning.Protocol.BOLT1 as BOLT1
import Lightning.Protocol.BOLT1
  ( ChannelId, Signature, Point, TlvRecord(..), TlvStream, TlvError )
import qualified Lightning.Protocol.BOLT9 as BOLT9
import Lightning.Protocol.BOLT2.Messages
import Lightning.Protocol.BOLT2.Types

-- errors ---------------------------------------------------------------------

-- | Why a message failed to encode.
data EncodeError
  = EncodeLengthOverflow
    -- ^ a length-prefixed field or a counted list exceeds 65535
  | EncodeInvalidTlvs
    -- ^ a @_tlvs@ field contains a record of a type the message knows
  | EncodeMessageTooLarge
    -- ^ the message (type and payload) exceeds 65535 bytes
  deriving (Eq, Show, Generic)

instance NFData EncodeError

-- | Why a message failed to decode.
data DecodeError
  = DecodeInsufficientBytes
    -- ^ the input ended before a field did
  | DecodeInvalidPoint
    -- ^ a point lacks a compressed-encoding prefix
  | DecodeInvalidAmount
    -- ^ an amount exceeds 21 million BTC
  | DecodeInvalidInitiator
    -- ^ the @initiator@ byte of @stfu@ is neither 0 nor 1
  | DecodeTlvError !TlvError
    -- ^ the message's TLV stream is malformed, or has an unknown even
    --   type
  | DecodeInvalidTlvValue !Word64
    -- ^ a known TLV record (of the given type) has a malformed value
  | DecodeUnknownEvenType !Word16
    -- ^ a message type that BOLT #2 doesn't define (even)
  | DecodeUnknownOddType !Word16
    -- ^ a message type that BOLT #2 doesn't define (odd)
  deriving (Eq, Show, Generic)

instance NFData DecodeError

-- parsing --------------------------------------------------------------------

newtype Parser a = Parser
  { run_parser :: BS.ByteString -> Either DecodeError (a, BS.ByteString) }

instance Functor Parser where
  fmap f (Parser p) = Parser $ \bs -> case p bs of
    Left e        -> Left e
    Right (a, r)  -> Right (f a, r)
  {-# INLINE fmap #-}

instance Applicative Parser where
  pure a = Parser $ \bs -> Right (a, bs)
  {-# INLINE pure #-}
  Parser pf <*> Parser pa = Parser $ \bs -> case pf bs of
    Left e       -> Left e
    Right (f, r) -> case pa r of
      Left e        -> Left e
      Right (a, r') -> Right (f a, r')
  {-# INLINE (<*>) #-}

instance Monad Parser where
  Parser p >>= k = Parser $ \bs -> case p bs of
    Left e       -> Left e
    Right (a, r) -> run_parser (k a) r
  {-# INLINE (>>=) #-}

-- run a parser whose last step consumes the remaining input
parse :: Parser a -> BS.ByteString -> Either DecodeError a
parse p bs = fmap fst (run_parser p bs)
{-# INLINE parse #-}

failure :: DecodeError -> Parser a
failure e = Parser $ \_ -> Left e
{-# INLINE failure #-}

-- lift a bolt1 field decoder, whose only failure is truncation
field :: (BS.ByteString -> Maybe (a, BS.ByteString)) -> Parser a
field f = Parser $ \bs -> case f bs of
  Nothing -> Left DecodeInsufficientBytes
  Just r  -> Right r
{-# INLINE field #-}

bytes :: Int -> Parser BS.ByteString
bytes n = Parser $ \bs ->
  if BS.length bs < n
    then Left DecodeInsufficientBytes
    else Right (BU.unsafeTake n bs, BU.unsafeDrop n bs)
{-# INLINE bytes #-}

-- a fixed-size field whose constructor checks only the length
sized :: Int -> (BS.ByteString -> Maybe a) -> Parser a
sized n f = do
  b <- bytes n
  maybe (failure DecodeInsufficientBytes) pure (f b)
{-# INLINE sized #-}

u8 :: Parser Word8
u8 = Parser $ \bs -> case BS.uncons bs of
  Nothing -> Left DecodeInsufficientBytes
  Just r  -> Right r
{-# INLINE u8 #-}

u16 :: Parser Word16
u16 = field BOLT1.decode_u16
{-# INLINE u16 #-}

u32 :: Parser Word32
u32 = field BOLT1.decode_u32
{-# INLINE u32 #-}

u64 :: Parser Word64
u64 = field BOLT1.decode_u64
{-# INLINE u64 #-}

chain_hash :: Parser BOLT1.ChainHash
chain_hash = field BOLT1.decode_chain_hash
{-# INLINE chain_hash #-}

channel_id :: Parser ChannelId
channel_id = field BOLT1.decode_channel_id
{-# INLINE channel_id #-}

signature :: Parser Signature
signature = field BOLT1.decode_signature
{-# INLINE signature #-}

payment_hash :: Parser BOLT1.PaymentHash
payment_hash = field BOLT1.decode_payment_hash
{-# INLINE payment_hash #-}

payment_preimage :: Parser BOLT1.PaymentPreimage
payment_preimage = field BOLT1.decode_payment_preimage
{-# INLINE payment_preimage #-}

per_commitment_secret :: Parser BOLT1.PerCommitmentSecret
per_commitment_secret = field BOLT1.decode_per_commitment_secret
{-# INLINE per_commitment_secret #-}

point :: Parser Point
point = do
  b <- bytes 33
  maybe (failure DecodeInvalidPoint) pure (BOLT1.point b)
{-# INLINE point #-}

satoshi :: Parser BOLT1.Satoshi
satoshi = do
  w <- u64
  maybe (failure DecodeInvalidAmount) pure (BOLT1.satoshi w)
{-# INLINE satoshi #-}

milli_satoshi :: Parser BOLT1.MilliSatoshi
milli_satoshi = do
  w <- u64
  maybe (failure DecodeInvalidAmount) pure (BOLT1.milli_satoshi w)
{-# INLINE milli_satoshi #-}

txid :: Parser TxId
txid = sized 32 mk_txid
{-# INLINE txid #-}

htlc_id :: Parser HtlcId
htlc_id = fmap HtlcId u64
{-# INLINE htlc_id #-}

serial_id :: Parser SerialId
serial_id = fmap SerialId u64
{-# INLINE serial_id #-}

prefixed :: Parser BS.ByteString
prefixed = field BOLT1.decode_u16_prefixed
{-# INLINE prefixed #-}

script :: Parser ScriptPubKey
script = do
  b <- prefixed
  maybe (failure DecodeInsufficientBytes) pure (script_pubkey b)
{-# INLINE script #-}

-- n items, each parsed by p
count :: Int -> Parser a -> Parser [a]
count n0 p = go n0 []
  where
    go !n !acc
      | n <= 0    = pure (reverse acc)
      | otherwise = p >>= \a -> go (n - 1) (a : acc)
{-# INLINE count #-}

-- the TLV stream occupying the rest of the payload, given the message's
-- known types
tlvs :: [Word64] -> Parser TlvStream
tlvs known = Parser $ \bs ->
  case BOLT1.decode_tlv_stream (`elem` known) bs of
    Left e  -> Left (DecodeTlvError e)
    Right s -> Right (s, BS.empty)
{-# INLINE tlvs #-}

-- the unknown records of a stream
unknown :: [Word64] -> TlvStream -> TlvStream
unknown known = BOLT1.filter_tlv_stream (`notElem` known)
{-# INLINE unknown #-}

-- the typed value of a known record, if present
record :: Word64 -> (BS.ByteString -> Maybe a) -> TlvStream -> Parser (Maybe a)
record t f s = case BOLT1.lookup_tlv t s of
  Nothing -> pure Nothing
  Just v  -> maybe (failure (DecodeInvalidTlvValue t)) (pure . Just) (f v)
{-# INLINE record #-}

-- whether a known record with an empty value is present
flag :: Word64 -> TlvStream -> Parser Bool
flag t s = fmap (maybe False (const True)) (record t empty_value s)
  where
    empty_value v
      | BS.null v = Just ()
      | otherwise = Nothing
{-# INLINE flag #-}

-- TLV values -----------------------------------------------------------------

-- run a field decoder over a whole TLV value
whole :: (BS.ByteString -> Maybe (a, BS.ByteString)) -> BS.ByteString
      -> Maybe a
whole f v = case f v of
  Just (a, r) | BS.null r -> Just a
  _ -> Nothing
{-# INLINE whole #-}

tlv_point :: BS.ByteString -> Maybe Point
tlv_point v
  | BS.length v == 33 = BOLT1.point v
  | otherwise         = Nothing

tlv_s64 :: BS.ByteString -> Maybe Int64
tlv_s64 = whole BOLT1.decode_s64

tlv_fee_range :: BS.ByteString -> Maybe FeeRange
tlv_fee_range v = do
  (lo, r0) <- BOLT1.decode_satoshi v
  hi <- whole BOLT1.decode_satoshi r0
  pure (FeeRange lo hi)

tlv_next_funding :: BS.ByteString -> Maybe NextFunding
tlv_next_funding v
  | BS.length v == 33 = do
      t <- mk_txid (BU.unsafeTake 32 v)
      pure (NextFunding t (BU.unsafeIndex v 32))
  | otherwise = Nothing

-- encoding -------------------------------------------------------------------

u16_prefixed :: BS.ByteString -> Either EncodeError BS.ByteString
u16_prefixed bs = maybe (Left EncodeLengthOverflow) Right
  (BOLT1.encode_u16_prefixed bs)
{-# INLINE u16_prefixed #-}

-- scripts are bounded, so their prefix can't overflow
script_bytes :: ScriptPubKey -> BS.ByteString
script_bytes s =
  let !b = un_script_pubkey s
  in  BOLT1.encode_u16 (fromIntegral (BS.length b)) <> b
{-# INLINE script_bytes #-}

count_prefix :: [a] -> Either EncodeError BS.ByteString
count_prefix xs
  | n > 65535 = Left EncodeLengthOverflow
  | otherwise = Right (BOLT1.encode_u16 (fromIntegral n))
  where
    !n = length xs
{-# INLINE count_prefix #-}

txid_bytes :: TxId -> BS.ByteString
txid_bytes = un_txid
{-# INLINE txid_bytes #-}

word8 :: Word8 -> BS.ByteString
word8 = BS.singleton
{-# INLINE word8 #-}

-- encode a message's TLV stream: its typed records plus the unknown
-- ones, which mustn't use a known type
encode_tlvs
  :: [Word64] -> [TlvRecord] -> TlvStream -> Either EncodeError BS.ByteString
encode_tlvs known recs extra
  | any ((`elem` known) . tlv_type) others = Left EncodeInvalidTlvs
  | otherwise = case BOLT1.tlv_stream (recs <> others) of
      Nothing -> Left EncodeInvalidTlvs
      Just s  -> Right (BOLT1.encode_tlv_stream s)
  where
    others = BOLT1.un_tlv_stream extra
{-# INLINE encode_tlvs #-}

opt :: Word64 -> (a -> BS.ByteString) -> Maybe a -> [TlvRecord]
opt t f = maybe [] (\a -> [TlvRecord t (f a)])
{-# INLINE opt #-}

opt_flag :: Word64 -> Bool -> [TlvRecord]
opt_flag t b = if b then [TlvRecord t BS.empty] else []
{-# INLINE opt_flag #-}

fee_range_bytes :: FeeRange -> BS.ByteString
fee_range_bytes (FeeRange lo hi) =
  BOLT1.encode_satoshi lo <> BOLT1.encode_satoshi hi

next_funding_bytes :: NextFunding -> BS.ByteString
next_funding_bytes (NextFunding t f) = txid_bytes t <> word8 f

-- messages -------------------------------------------------------------------

-- | Encode a BOLT #2 message, including its type. Fails if a field
--   can't be encoded or the result exceeds 65535 bytes.
--
--   >>> let stfu = Stfu cid True BOLT1.empty_tlv_stream
--   >>> fmap BS.length (encode_message (MsgStfu stfu))
--   Right 35
encode_message :: Message -> Either EncodeError BS.ByteString
encode_message m = do
  payload <- case m of
    MsgOpenChannel a             -> encode_open_channel a
    MsgAcceptChannel a           -> encode_accept_channel a
    MsgFundingCreated a          -> Right (encode_funding_created a)
    MsgFundingSigned a           -> Right (encode_funding_signed a)
    MsgChannelReady a            -> encode_channel_ready a
    MsgOpenChannel2 a            -> encode_open_channel2 a
    MsgAcceptChannel2 a          -> encode_accept_channel2 a
    MsgTxAddInput a              -> encode_tx_add_input a
    MsgTxAddOutput a             -> Right (encode_tx_add_output a)
    MsgTxRemoveInput a           -> Right (encode_tx_remove_input a)
    MsgTxRemoveOutput a          -> Right (encode_tx_remove_output a)
    MsgTxComplete a              -> Right (encode_tx_complete a)
    MsgTxSignatures a            -> encode_tx_signatures a
    MsgTxInitRbf a               -> encode_tx_init_rbf a
    MsgTxAckRbf a                -> encode_tx_ack_rbf a
    MsgTxAbort a                 -> encode_tx_abort a
    MsgStfu a                    -> Right (encode_stfu a)
    MsgShutdown a                -> Right (encode_shutdown a)
    MsgClosingComplete a         -> encode_closing_complete a
    MsgClosingSig a              -> encode_closing_sig a
    MsgClosingSigned a           -> encode_closing_signed a
    MsgUpdateAddHtlc a           -> encode_update_add_htlc a
    MsgUpdateFulfillHtlc a       -> encode_update_fulfill_htlc a
    MsgUpdateFailHtlc a          -> encode_update_fail_htlc a
    MsgUpdateFailMalformedHtlc a ->
      Right (encode_update_fail_malformed_htlc a)
    MsgCommitmentSigned a        -> encode_commitment_signed a
    MsgRevokeAndAck a            -> Right (encode_revoke_and_ack a)
    MsgUpdateFee a               -> Right (encode_update_fee a)
    MsgChannelReestablish a      -> encode_channel_reestablish a
  case BOLT1.encode_envelope (message_type m) payload of
    Left _  -> Left EncodeMessageTooLarge
    Right w -> Right w

-- | Decode a BOLT #2 message, including its type.
--
--   A type that BOLT #2 doesn't define (including the splicing
--   messages, which this library doesn't implement) yields
--   'DecodeUnknownEvenType' or 'DecodeUnknownOddType'.
--
--   >>> let wire = "\x00\x02" <> BS.replicate 32 0xab <> "\x01"
--   >>> fmap message_type (decode_message wire)
--   Right 2
--   >>> decode_message "\x00\x50"
--   Left (DecodeUnknownEvenType 80)
decode_message :: BS.ByteString -> Either DecodeError Message
decode_message bs = do
  (t, payload) <- case BOLT1.decode_envelope bs of
    Left _  -> Left DecodeInsufficientBytes
    Right r -> Right r
  case t of
    32  -> MsgOpenChannel <$> decode_open_channel payload
    33  -> MsgAcceptChannel <$> decode_accept_channel payload
    34  -> MsgFundingCreated <$> decode_funding_created payload
    35  -> MsgFundingSigned <$> decode_funding_signed payload
    36  -> MsgChannelReady <$> decode_channel_ready payload
    64  -> MsgOpenChannel2 <$> decode_open_channel2 payload
    65  -> MsgAcceptChannel2 <$> decode_accept_channel2 payload
    66  -> MsgTxAddInput <$> decode_tx_add_input payload
    67  -> MsgTxAddOutput <$> decode_tx_add_output payload
    68  -> MsgTxRemoveInput <$> decode_tx_remove_input payload
    69  -> MsgTxRemoveOutput <$> decode_tx_remove_output payload
    70  -> MsgTxComplete <$> decode_tx_complete payload
    71  -> MsgTxSignatures <$> decode_tx_signatures payload
    72  -> MsgTxInitRbf <$> decode_tx_init_rbf payload
    73  -> MsgTxAckRbf <$> decode_tx_ack_rbf payload
    74  -> MsgTxAbort <$> decode_tx_abort payload
    2   -> MsgStfu <$> decode_stfu payload
    38  -> MsgShutdown <$> decode_shutdown payload
    40  -> MsgClosingComplete <$> decode_closing_complete payload
    41  -> MsgClosingSig <$> decode_closing_sig payload
    39  -> MsgClosingSigned <$> decode_closing_signed payload
    128 -> MsgUpdateAddHtlc <$> decode_update_add_htlc payload
    130 -> MsgUpdateFulfillHtlc <$> decode_update_fulfill_htlc payload
    131 -> MsgUpdateFailHtlc <$> decode_update_fail_htlc payload
    135 -> MsgUpdateFailMalformedHtlc <$>
             decode_update_fail_malformed_htlc payload
    132 -> MsgCommitmentSigned <$> decode_commitment_signed payload
    133 -> MsgRevokeAndAck <$> decode_revoke_and_ack payload
    134 -> MsgUpdateFee <$> decode_update_fee payload
    136 -> MsgChannelReestablish <$> decode_channel_reestablish payload
    _ | even t    -> Left (DecodeUnknownEvenType t)
      | otherwise -> Left (DecodeUnknownOddType t)

-- open_channel ---------------------------------------------------------------

open_channel_known :: [Word64]
open_channel_known = [0, 1]

-- | Encode an t'OpenChannel' payload.
encode_open_channel :: OpenChannel -> Either EncodeError BS.ByteString
encode_open_channel m = do
  ts <- encode_tlvs open_channel_known
    (  opt 0 un_script_pubkey (open_channel_upfront_shutdown_script m)
    <> opt 1 BOLT9.render (open_channel_channel_type m) )
    (open_channel_tlvs m)
  pure $ mconcat [
      BOLT1.un_chain_hash (open_channel_chain_hash m)
    , BOLT1.un_channel_id (open_channel_temporary_channel_id m)
    , BOLT1.encode_satoshi (open_channel_funding_satoshis m)
    , BOLT1.encode_milli_satoshi (open_channel_push_msat m)
    , BOLT1.encode_satoshi (open_channel_dust_limit_satoshis m)
    , BOLT1.encode_u64 (open_channel_max_htlc_value_in_flight_msat m)
    , BOLT1.encode_satoshi (open_channel_channel_reserve_satoshis m)
    , BOLT1.encode_milli_satoshi (open_channel_htlc_minimum_msat m)
    , BOLT1.encode_u32 (open_channel_feerate_per_kw m)
    , BOLT1.encode_u16 (open_channel_to_self_delay m)
    , BOLT1.encode_u16 (open_channel_max_accepted_htlcs m)
    , BOLT1.un_point (open_channel_funding_pubkey m)
    , BOLT1.un_point (open_channel_revocation_basepoint m)
    , BOLT1.un_point (open_channel_payment_basepoint m)
    , BOLT1.un_point (open_channel_delayed_payment_basepoint m)
    , BOLT1.un_point (open_channel_htlc_basepoint m)
    , BOLT1.un_point (open_channel_first_per_commitment_point m)
    , word8 (open_channel_channel_flags m)
    , ts
    ]

-- | Decode an t'OpenChannel' payload.
decode_open_channel :: BS.ByteString -> Either DecodeError OpenChannel
decode_open_channel = parse $ do
  ch    <- chain_hash
  tcid  <- channel_id
  fund  <- satoshi
  push  <- milli_satoshi
  dust  <- satoshi
  maxv  <- u64
  res   <- satoshi
  hmin  <- milli_satoshi
  fee   <- u32
  delay <- u16
  maxa  <- u16
  fpk   <- point
  rev   <- point
  pay   <- point
  del   <- point
  htlc  <- point
  pcp   <- point
  flags <- u8
  s     <- tlvs open_channel_known
  shut  <- record 0 script_pubkey s
  ctype <- record 1 (Just . BOLT9.parse) s
  pure OpenChannel {
      open_channel_chain_hash                    = ch
    , open_channel_temporary_channel_id          = tcid
    , open_channel_funding_satoshis              = fund
    , open_channel_push_msat                     = push
    , open_channel_dust_limit_satoshis           = dust
    , open_channel_max_htlc_value_in_flight_msat = maxv
    , open_channel_channel_reserve_satoshis      = res
    , open_channel_htlc_minimum_msat             = hmin
    , open_channel_feerate_per_kw                = fee
    , open_channel_to_self_delay                 = delay
    , open_channel_max_accepted_htlcs            = maxa
    , open_channel_funding_pubkey                = fpk
    , open_channel_revocation_basepoint          = rev
    , open_channel_payment_basepoint             = pay
    , open_channel_delayed_payment_basepoint     = del
    , open_channel_htlc_basepoint                = htlc
    , open_channel_first_per_commitment_point    = pcp
    , open_channel_channel_flags                 = flags
    , open_channel_upfront_shutdown_script       = shut
    , open_channel_channel_type                  = ctype
    , open_channel_tlvs                          = unknown open_channel_known s
    }

-- accept_channel -------------------------------------------------------------

accept_channel_known :: [Word64]
accept_channel_known = [0, 1]

-- | Encode an t'AcceptChannel' payload.
encode_accept_channel :: AcceptChannel -> Either EncodeError BS.ByteString
encode_accept_channel m = do
  ts <- encode_tlvs accept_channel_known
    (  opt 0 un_script_pubkey (accept_channel_upfront_shutdown_script m)
    <> opt 1 BOLT9.render (accept_channel_channel_type m) )
    (accept_channel_tlvs m)
  pure $ mconcat [
      BOLT1.un_channel_id (accept_channel_temporary_channel_id m)
    , BOLT1.encode_satoshi (accept_channel_dust_limit_satoshis m)
    , BOLT1.encode_u64 (accept_channel_max_htlc_value_in_flight_msat m)
    , BOLT1.encode_satoshi (accept_channel_channel_reserve_satoshis m)
    , BOLT1.encode_milli_satoshi (accept_channel_htlc_minimum_msat m)
    , BOLT1.encode_u32 (accept_channel_minimum_depth m)
    , BOLT1.encode_u16 (accept_channel_to_self_delay m)
    , BOLT1.encode_u16 (accept_channel_max_accepted_htlcs m)
    , BOLT1.un_point (accept_channel_funding_pubkey m)
    , BOLT1.un_point (accept_channel_revocation_basepoint m)
    , BOLT1.un_point (accept_channel_payment_basepoint m)
    , BOLT1.un_point (accept_channel_delayed_payment_basepoint m)
    , BOLT1.un_point (accept_channel_htlc_basepoint m)
    , BOLT1.un_point (accept_channel_first_per_commitment_point m)
    , ts
    ]

-- | Decode an t'AcceptChannel' payload.
decode_accept_channel :: BS.ByteString -> Either DecodeError AcceptChannel
decode_accept_channel = parse $ do
  tcid  <- channel_id
  dust  <- satoshi
  maxv  <- u64
  res   <- satoshi
  hmin  <- milli_satoshi
  depth <- u32
  delay <- u16
  maxa  <- u16
  fpk   <- point
  rev   <- point
  pay   <- point
  del   <- point
  htlc  <- point
  pcp   <- point
  s     <- tlvs accept_channel_known
  shut  <- record 0 script_pubkey s
  ctype <- record 1 (Just . BOLT9.parse) s
  pure AcceptChannel {
      accept_channel_temporary_channel_id          = tcid
    , accept_channel_dust_limit_satoshis           = dust
    , accept_channel_max_htlc_value_in_flight_msat = maxv
    , accept_channel_channel_reserve_satoshis      = res
    , accept_channel_htlc_minimum_msat             = hmin
    , accept_channel_minimum_depth                 = depth
    , accept_channel_to_self_delay                 = delay
    , accept_channel_max_accepted_htlcs            = maxa
    , accept_channel_funding_pubkey                = fpk
    , accept_channel_revocation_basepoint          = rev
    , accept_channel_payment_basepoint             = pay
    , accept_channel_delayed_payment_basepoint     = del
    , accept_channel_htlc_basepoint                = htlc
    , accept_channel_first_per_commitment_point    = pcp
    , accept_channel_upfront_shutdown_script       = shut
    , accept_channel_channel_type                  = ctype
    , accept_channel_tlvs = unknown accept_channel_known s
    }

-- funding_created, funding_signed --------------------------------------------

-- | Encode a t'FundingCreated' payload.
encode_funding_created :: FundingCreated -> BS.ByteString
encode_funding_created m = mconcat [
    BOLT1.un_channel_id (funding_created_temporary_channel_id m)
  , txid_bytes (funding_created_funding_txid m)
  , BOLT1.encode_u16 (funding_created_funding_output_index m)
  , BOLT1.un_signature (funding_created_signature m)
  , BOLT1.encode_tlv_stream (funding_created_tlvs m)
  ]

-- | Decode a t'FundingCreated' payload.
decode_funding_created :: BS.ByteString -> Either DecodeError FundingCreated
decode_funding_created = parse $
  FundingCreated <$> channel_id <*> txid <*> u16 <*> signature <*> tlvs []

-- | Encode a t'FundingSigned' payload.
encode_funding_signed :: FundingSigned -> BS.ByteString
encode_funding_signed m = mconcat [
    BOLT1.un_channel_id (funding_signed_channel_id m)
  , BOLT1.un_signature (funding_signed_signature m)
  , BOLT1.encode_tlv_stream (funding_signed_tlvs m)
  ]

-- | Decode a t'FundingSigned' payload.
decode_funding_signed :: BS.ByteString -> Either DecodeError FundingSigned
decode_funding_signed = parse $
  FundingSigned <$> channel_id <*> signature <*> tlvs []

-- channel_ready --------------------------------------------------------------

channel_ready_known :: [Word64]
channel_ready_known = [1]

-- | Encode a t'ChannelReady' payload.
encode_channel_ready :: ChannelReady -> Either EncodeError BS.ByteString
encode_channel_ready m = do
  ts <- encode_tlvs channel_ready_known
    (opt 1 BOLT1.encode_short_channel_id (channel_ready_alias m))
    (channel_ready_tlvs m)
  pure $ mconcat [
      BOLT1.un_channel_id (channel_ready_channel_id m)
    , BOLT1.un_point (channel_ready_second_per_commitment_point m)
    , ts
    ]

-- | Decode a t'ChannelReady' payload.
decode_channel_ready :: BS.ByteString -> Either DecodeError ChannelReady
decode_channel_ready = parse $ do
  cid   <- channel_id
  pcp   <- point
  s     <- tlvs channel_ready_known
  alias <- record 1 (whole BOLT1.decode_short_channel_id) s
  pure (ChannelReady cid pcp alias (unknown channel_ready_known s))

-- open_channel2 --------------------------------------------------------------

opening_known :: [Word64]
opening_known = [0, 1, 2]

-- | Encode an t'OpenChannel2' payload.
encode_open_channel2 :: OpenChannel2 -> Either EncodeError BS.ByteString
encode_open_channel2 m = do
  ts <- encode_tlvs opening_known
    (  opt 0 un_script_pubkey (open_channel2_upfront_shutdown_script m)
    <> opt 1 BOLT9.render (open_channel2_channel_type m)
    <> opt_flag 2 (open_channel2_require_confirmed_inputs m) )
    (open_channel2_tlvs m)
  pure $ mconcat [
      BOLT1.un_chain_hash (open_channel2_chain_hash m)
    , BOLT1.un_channel_id (open_channel2_temporary_channel_id m)
    , BOLT1.encode_u32 (open_channel2_funding_feerate_perkw m)
    , BOLT1.encode_u32 (open_channel2_commitment_feerate_perkw m)
    , BOLT1.encode_satoshi (open_channel2_funding_satoshis m)
    , BOLT1.encode_satoshi (open_channel2_dust_limit_satoshis m)
    , BOLT1.encode_u64 (open_channel2_max_htlc_value_in_flight_msat m)
    , BOLT1.encode_milli_satoshi (open_channel2_htlc_minimum_msat m)
    , BOLT1.encode_u16 (open_channel2_to_self_delay m)
    , BOLT1.encode_u16 (open_channel2_max_accepted_htlcs m)
    , BOLT1.encode_u32 (open_channel2_locktime m)
    , BOLT1.un_point (open_channel2_funding_pubkey m)
    , BOLT1.un_point (open_channel2_revocation_basepoint m)
    , BOLT1.un_point (open_channel2_payment_basepoint m)
    , BOLT1.un_point (open_channel2_delayed_payment_basepoint m)
    , BOLT1.un_point (open_channel2_htlc_basepoint m)
    , BOLT1.un_point (open_channel2_first_per_commitment_point m)
    , BOLT1.un_point (open_channel2_second_per_commitment_point m)
    , word8 (open_channel2_channel_flags m)
    , ts
    ]

-- | Decode an t'OpenChannel2' payload.
decode_open_channel2 :: BS.ByteString -> Either DecodeError OpenChannel2
decode_open_channel2 = parse $ do
  ch    <- chain_hash
  tcid  <- channel_id
  ffee  <- u32
  cfee  <- u32
  fund  <- satoshi
  dust  <- satoshi
  maxv  <- u64
  hmin  <- milli_satoshi
  delay <- u16
  maxa  <- u16
  lock  <- u32
  fpk   <- point
  rev   <- point
  pay   <- point
  del   <- point
  htlc  <- point
  pcp1  <- point
  pcp2  <- point
  flags <- u8
  s     <- tlvs opening_known
  shut  <- record 0 script_pubkey s
  ctype <- record 1 (Just . BOLT9.parse) s
  conf  <- flag 2 s
  pure OpenChannel2 {
      open_channel2_chain_hash                    = ch
    , open_channel2_temporary_channel_id          = tcid
    , open_channel2_funding_feerate_perkw         = ffee
    , open_channel2_commitment_feerate_perkw      = cfee
    , open_channel2_funding_satoshis              = fund
    , open_channel2_dust_limit_satoshis           = dust
    , open_channel2_max_htlc_value_in_flight_msat = maxv
    , open_channel2_htlc_minimum_msat             = hmin
    , open_channel2_to_self_delay                 = delay
    , open_channel2_max_accepted_htlcs            = maxa
    , open_channel2_locktime                      = lock
    , open_channel2_funding_pubkey                = fpk
    , open_channel2_revocation_basepoint          = rev
    , open_channel2_payment_basepoint             = pay
    , open_channel2_delayed_payment_basepoint     = del
    , open_channel2_htlc_basepoint                = htlc
    , open_channel2_first_per_commitment_point    = pcp1
    , open_channel2_second_per_commitment_point   = pcp2
    , open_channel2_channel_flags                 = flags
    , open_channel2_upfront_shutdown_script       = shut
    , open_channel2_channel_type                  = ctype
    , open_channel2_require_confirmed_inputs      = conf
    , open_channel2_tlvs                          = unknown opening_known s
    }

-- accept_channel2 ------------------------------------------------------------

-- | Encode an t'AcceptChannel2' payload.
encode_accept_channel2 :: AcceptChannel2 -> Either EncodeError BS.ByteString
encode_accept_channel2 m = do
  ts <- encode_tlvs opening_known
    (  opt 0 un_script_pubkey (accept_channel2_upfront_shutdown_script m)
    <> opt 1 BOLT9.render (accept_channel2_channel_type m)
    <> opt_flag 2 (accept_channel2_require_confirmed_inputs m) )
    (accept_channel2_tlvs m)
  pure $ mconcat [
      BOLT1.un_channel_id (accept_channel2_temporary_channel_id m)
    , BOLT1.encode_satoshi (accept_channel2_funding_satoshis m)
    , BOLT1.encode_satoshi (accept_channel2_dust_limit_satoshis m)
    , BOLT1.encode_u64 (accept_channel2_max_htlc_value_in_flight_msat m)
    , BOLT1.encode_milli_satoshi (accept_channel2_htlc_minimum_msat m)
    , BOLT1.encode_u32 (accept_channel2_minimum_depth m)
    , BOLT1.encode_u16 (accept_channel2_to_self_delay m)
    , BOLT1.encode_u16 (accept_channel2_max_accepted_htlcs m)
    , BOLT1.un_point (accept_channel2_funding_pubkey m)
    , BOLT1.un_point (accept_channel2_revocation_basepoint m)
    , BOLT1.un_point (accept_channel2_payment_basepoint m)
    , BOLT1.un_point (accept_channel2_delayed_payment_basepoint m)
    , BOLT1.un_point (accept_channel2_htlc_basepoint m)
    , BOLT1.un_point (accept_channel2_first_per_commitment_point m)
    , BOLT1.un_point (accept_channel2_second_per_commitment_point m)
    , ts
    ]

-- | Decode an t'AcceptChannel2' payload.
decode_accept_channel2 :: BS.ByteString -> Either DecodeError AcceptChannel2
decode_accept_channel2 = parse $ do
  tcid  <- channel_id
  fund  <- satoshi
  dust  <- satoshi
  maxv  <- u64
  hmin  <- milli_satoshi
  depth <- u32
  delay <- u16
  maxa  <- u16
  fpk   <- point
  rev   <- point
  pay   <- point
  del   <- point
  htlc  <- point
  pcp1  <- point
  pcp2  <- point
  s     <- tlvs opening_known
  shut  <- record 0 script_pubkey s
  ctype <- record 1 (Just . BOLT9.parse) s
  conf  <- flag 2 s
  pure AcceptChannel2 {
      accept_channel2_temporary_channel_id          = tcid
    , accept_channel2_funding_satoshis              = fund
    , accept_channel2_dust_limit_satoshis           = dust
    , accept_channel2_max_htlc_value_in_flight_msat = maxv
    , accept_channel2_htlc_minimum_msat             = hmin
    , accept_channel2_minimum_depth                 = depth
    , accept_channel2_to_self_delay                 = delay
    , accept_channel2_max_accepted_htlcs            = maxa
    , accept_channel2_funding_pubkey                = fpk
    , accept_channel2_revocation_basepoint          = rev
    , accept_channel2_payment_basepoint             = pay
    , accept_channel2_delayed_payment_basepoint     = del
    , accept_channel2_htlc_basepoint                = htlc
    , accept_channel2_first_per_commitment_point    = pcp1
    , accept_channel2_second_per_commitment_point   = pcp2
    , accept_channel2_upfront_shutdown_script       = shut
    , accept_channel2_channel_type                  = ctype
    , accept_channel2_require_confirmed_inputs      = conf
    , accept_channel2_tlvs = unknown opening_known s
    }

-- interactive transaction construction ---------------------------------------

-- | Encode a t'TxAddInput' payload. Fails if @prevtx@ exceeds 65535
--   bytes.
encode_tx_add_input :: TxAddInput -> Either EncodeError BS.ByteString
encode_tx_add_input m = do
  prev <- u16_prefixed (tx_add_input_prevtx m)
  let SerialId sid = tx_add_input_serial_id m
  pure $ mconcat [
      BOLT1.un_channel_id (tx_add_input_channel_id m)
    , BOLT1.encode_u64 sid
    , prev
    , BOLT1.encode_u32 (tx_add_input_prevtx_vout m)
    , BOLT1.encode_u32 (tx_add_input_sequence m)
    , BOLT1.encode_tlv_stream (tx_add_input_tlvs m)
    ]

-- | Decode a t'TxAddInput' payload.
decode_tx_add_input :: BS.ByteString -> Either DecodeError TxAddInput
decode_tx_add_input = parse $
  TxAddInput <$> channel_id <*> serial_id <*> prefixed <*> u32 <*> u32
             <*> tlvs []

-- | Encode a t'TxAddOutput' payload.
encode_tx_add_output :: TxAddOutput -> BS.ByteString
encode_tx_add_output m =
  let SerialId sid = tx_add_output_serial_id m
  in  mconcat [
          BOLT1.un_channel_id (tx_add_output_channel_id m)
        , BOLT1.encode_u64 sid
        , BOLT1.encode_satoshi (tx_add_output_sats m)
        , script_bytes (tx_add_output_script m)
        , BOLT1.encode_tlv_stream (tx_add_output_tlvs m)
        ]

-- | Decode a t'TxAddOutput' payload.
decode_tx_add_output :: BS.ByteString -> Either DecodeError TxAddOutput
decode_tx_add_output = parse $
  TxAddOutput <$> channel_id <*> serial_id <*> satoshi <*> script
              <*> tlvs []

-- | Encode a t'TxRemoveInput' payload.
encode_tx_remove_input :: TxRemoveInput -> BS.ByteString
encode_tx_remove_input (TxRemoveInput cid (SerialId sid) ts) = mconcat [
    BOLT1.un_channel_id cid
  , BOLT1.encode_u64 sid
  , BOLT1.encode_tlv_stream ts
  ]

-- | Decode a t'TxRemoveInput' payload.
decode_tx_remove_input :: BS.ByteString -> Either DecodeError TxRemoveInput
decode_tx_remove_input = parse $
  TxRemoveInput <$> channel_id <*> serial_id <*> tlvs []

-- | Encode a t'TxRemoveOutput' payload.
encode_tx_remove_output :: TxRemoveOutput -> BS.ByteString
encode_tx_remove_output (TxRemoveOutput cid (SerialId sid) ts) = mconcat [
    BOLT1.un_channel_id cid
  , BOLT1.encode_u64 sid
  , BOLT1.encode_tlv_stream ts
  ]

-- | Decode a t'TxRemoveOutput' payload.
decode_tx_remove_output
  :: BS.ByteString -> Either DecodeError TxRemoveOutput
decode_tx_remove_output = parse $
  TxRemoveOutput <$> channel_id <*> serial_id <*> tlvs []

-- | Encode a t'TxComplete' payload.
encode_tx_complete :: TxComplete -> BS.ByteString
encode_tx_complete (TxComplete cid ts) =
  BOLT1.un_channel_id cid <> BOLT1.encode_tlv_stream ts

-- | Decode a t'TxComplete' payload.
decode_tx_complete :: BS.ByteString -> Either DecodeError TxComplete
decode_tx_complete = parse $ TxComplete <$> channel_id <*> tlvs []

-- | Encode a t'TxSignatures' payload. Fails if there are more than 65535
--   witnesses.
encode_tx_signatures :: TxSignatures -> Either EncodeError BS.ByteString
encode_tx_signatures m = do
  let ws = tx_signatures_witnesses m
  n <- count_prefix ws
  pure $ mconcat [
      BOLT1.un_channel_id (tx_signatures_channel_id m)
    , txid_bytes (tx_signatures_txid m)
    , n
    , mconcat [ BOLT1.encode_u16 (fromIntegral (BS.length w)) <> w
              | w <- map un_witness ws ]
    , BOLT1.encode_tlv_stream (tx_signatures_tlvs m)
    ]

-- | Decode a t'TxSignatures' payload.
decode_tx_signatures :: BS.ByteString -> Either DecodeError TxSignatures
decode_tx_signatures = parse $ do
  cid <- channel_id
  t   <- txid
  n   <- u16
  ws  <- count (fromIntegral n) wit
  TxSignatures cid t ws <$> tlvs []
  where
    wit = do
      b <- prefixed
      maybe (failure DecodeInsufficientBytes) pure (witness b)

rbf_known :: [Word64]
rbf_known = [0, 2]

-- | Encode a t'TxInitRbf' payload.
encode_tx_init_rbf :: TxInitRbf -> Either EncodeError BS.ByteString
encode_tx_init_rbf m = do
  ts <- encode_tlvs rbf_known
    (  opt 0 BOLT1.encode_s64 (tx_init_rbf_funding_output_contribution m)
    <> opt_flag 2 (tx_init_rbf_require_confirmed_inputs m) )
    (tx_init_rbf_tlvs m)
  pure $ mconcat [
      BOLT1.un_channel_id (tx_init_rbf_channel_id m)
    , BOLT1.encode_u32 (tx_init_rbf_locktime m)
    , BOLT1.encode_u32 (tx_init_rbf_feerate m)
    , ts
    ]

-- | Decode a t'TxInitRbf' payload.
decode_tx_init_rbf :: BS.ByteString -> Either DecodeError TxInitRbf
decode_tx_init_rbf = parse $ do
  cid  <- channel_id
  lock <- u32
  fee  <- u32
  s    <- tlvs rbf_known
  contrib <- record 0 tlv_s64 s
  conf <- flag 2 s
  pure (TxInitRbf cid lock fee contrib conf (unknown rbf_known s))

-- | Encode a t'TxAckRbf' payload.
encode_tx_ack_rbf :: TxAckRbf -> Either EncodeError BS.ByteString
encode_tx_ack_rbf m = do
  ts <- encode_tlvs rbf_known
    (  opt 0 BOLT1.encode_s64 (tx_ack_rbf_funding_output_contribution m)
    <> opt_flag 2 (tx_ack_rbf_require_confirmed_inputs m) )
    (tx_ack_rbf_tlvs m)
  pure (BOLT1.un_channel_id (tx_ack_rbf_channel_id m) <> ts)

-- | Decode a t'TxAckRbf' payload.
decode_tx_ack_rbf :: BS.ByteString -> Either DecodeError TxAckRbf
decode_tx_ack_rbf = parse $ do
  cid  <- channel_id
  s    <- tlvs rbf_known
  contrib <- record 0 tlv_s64 s
  conf <- flag 2 s
  pure (TxAckRbf cid contrib conf (unknown rbf_known s))

-- | Encode a t'TxAbort' payload. Fails if @data@ exceeds 65535 bytes.
encode_tx_abort :: TxAbort -> Either EncodeError BS.ByteString
encode_tx_abort (TxAbort cid dat ts) = do
  dat' <- u16_prefixed dat
  pure (BOLT1.un_channel_id cid <> dat' <> BOLT1.encode_tlv_stream ts)

-- | Decode a t'TxAbort' payload.
decode_tx_abort :: BS.ByteString -> Either DecodeError TxAbort
decode_tx_abort = parse $ TxAbort <$> channel_id <*> prefixed <*> tlvs []

-- stfu -----------------------------------------------------------------------

-- | Encode a t'Stfu' payload.
encode_stfu :: Stfu -> BS.ByteString
encode_stfu (Stfu cid ini ts) = mconcat [
    BOLT1.un_channel_id cid
  , word8 (if ini then 1 else 0)
  , BOLT1.encode_tlv_stream ts
  ]

-- | Decode a t'Stfu' payload. The @initiator@ byte must be 0 or 1.
decode_stfu :: BS.ByteString -> Either DecodeError Stfu
decode_stfu = parse $ do
  cid <- channel_id
  b   <- u8
  ini <- case b of
    0 -> pure False
    1 -> pure True
    _ -> failure DecodeInvalidInitiator
  Stfu cid ini <$> tlvs []

-- shutdown -------------------------------------------------------------------

-- | Encode a t'Shutdown' payload.
encode_shutdown :: Shutdown -> BS.ByteString
encode_shutdown (Shutdown cid spk ts) = mconcat [
    BOLT1.un_channel_id cid
  , script_bytes spk
  , BOLT1.encode_tlv_stream ts
  ]

-- | Decode a t'Shutdown' payload.
decode_shutdown :: BS.ByteString -> Either DecodeError Shutdown
decode_shutdown = parse $ Shutdown <$> channel_id <*> script <*> tlvs []

-- closing_complete, closing_sig ----------------------------------------------

closing_known :: [Word64]
closing_known = [1, 2, 3]

closing_records
  :: Maybe Signature -> Maybe Signature -> Maybe Signature -> [TlvRecord]
closing_records a b c =
     opt 1 BOLT1.un_signature a
  <> opt 2 BOLT1.un_signature b
  <> opt 3 BOLT1.un_signature c

-- | Encode a t'ClosingComplete' payload.
encode_closing_complete :: ClosingComplete -> Either EncodeError BS.ByteString
encode_closing_complete m = do
  ts <- encode_tlvs closing_known
    (closing_records
      (closing_complete_closer_output_only m)
      (closing_complete_closee_output_only m)
      (closing_complete_closer_and_closee_outputs m))
    (closing_complete_tlvs m)
  pure $ mconcat [
      BOLT1.un_channel_id (closing_complete_channel_id m)
    , script_bytes (closing_complete_closer_scriptpubkey m)
    , script_bytes (closing_complete_closee_scriptpubkey m)
    , BOLT1.encode_satoshi (closing_complete_fee_satoshis m)
    , BOLT1.encode_u32 (closing_complete_locktime m)
    , ts
    ]

-- | Decode a t'ClosingComplete' payload.
decode_closing_complete
  :: BS.ByteString -> Either DecodeError ClosingComplete
decode_closing_complete = parse $ do
  cid    <- channel_id
  closer <- script
  closee <- script
  fee    <- satoshi
  lock   <- u32
  s      <- tlvs closing_known
  a      <- record 1 BOLT1.signature s
  b      <- record 2 BOLT1.signature s
  c      <- record 3 BOLT1.signature s
  pure (ClosingComplete cid closer closee fee lock a b c
          (unknown closing_known s))

-- | Encode a t'ClosingSig' payload.
encode_closing_sig :: ClosingSig -> Either EncodeError BS.ByteString
encode_closing_sig m = do
  ts <- encode_tlvs closing_known
    (closing_records
      (closing_sig_closer_output_only m)
      (closing_sig_closee_output_only m)
      (closing_sig_closer_and_closee_outputs m))
    (closing_sig_tlvs m)
  pure $ mconcat [
      BOLT1.un_channel_id (closing_sig_channel_id m)
    , script_bytes (closing_sig_closer_scriptpubkey m)
    , script_bytes (closing_sig_closee_scriptpubkey m)
    , BOLT1.encode_satoshi (closing_sig_fee_satoshis m)
    , BOLT1.encode_u32 (closing_sig_locktime m)
    , ts
    ]

-- | Decode a t'ClosingSig' payload.
decode_closing_sig :: BS.ByteString -> Either DecodeError ClosingSig
decode_closing_sig = parse $ do
  cid    <- channel_id
  closer <- script
  closee <- script
  fee    <- satoshi
  lock   <- u32
  s      <- tlvs closing_known
  a      <- record 1 BOLT1.signature s
  b      <- record 2 BOLT1.signature s
  c      <- record 3 BOLT1.signature s
  pure (ClosingSig cid closer closee fee lock a b c
          (unknown closing_known s))

-- closing_signed -------------------------------------------------------------

closing_signed_known :: [Word64]
closing_signed_known = [1]

-- | Encode a t'ClosingSigned' payload.
encode_closing_signed :: ClosingSigned -> Either EncodeError BS.ByteString
encode_closing_signed m = do
  ts <- encode_tlvs closing_signed_known
    (opt 1 fee_range_bytes (closing_signed_fee_range m))
    (closing_signed_tlvs m)
  pure $ mconcat [
      BOLT1.un_channel_id (closing_signed_channel_id m)
    , BOLT1.encode_satoshi (closing_signed_fee_satoshis m)
    , BOLT1.un_signature (closing_signed_signature m)
    , ts
    ]

-- | Decode a t'ClosingSigned' payload.
decode_closing_signed :: BS.ByteString -> Either DecodeError ClosingSigned
decode_closing_signed = parse $ do
  cid   <- channel_id
  fee   <- satoshi
  sig   <- signature
  s     <- tlvs closing_signed_known
  range <- record 1 tlv_fee_range s
  pure (ClosingSigned cid fee sig range (unknown closing_signed_known s))

-- update_add_htlc ------------------------------------------------------------

update_add_htlc_known :: [Word64]
update_add_htlc_known = [0]

-- | Encode an t'UpdateAddHtlc' payload.
encode_update_add_htlc :: UpdateAddHtlc -> Either EncodeError BS.ByteString
encode_update_add_htlc m = do
  ts <- encode_tlvs update_add_htlc_known
    (opt 0 BOLT1.un_point (update_add_htlc_path_key m))
    (update_add_htlc_tlvs m)
  let HtlcId hid = update_add_htlc_id m
  pure $ mconcat [
      BOLT1.un_channel_id (update_add_htlc_channel_id m)
    , BOLT1.encode_u64 hid
    , BOLT1.encode_milli_satoshi (update_add_htlc_amount_msat m)
    , BOLT1.un_payment_hash (update_add_htlc_payment_hash m)
    , BOLT1.encode_u32 (update_add_htlc_cltv_expiry m)
    , un_onion_routing_packet (update_add_htlc_onion_routing_packet m)
    , ts
    ]

-- | Decode an t'UpdateAddHtlc' payload.
decode_update_add_htlc :: BS.ByteString -> Either DecodeError UpdateAddHtlc
decode_update_add_htlc = parse $ do
  cid   <- channel_id
  hid   <- htlc_id
  amt   <- milli_satoshi
  ph    <- payment_hash
  cltv  <- u32
  onion <- sized 1366 onion_routing_packet
  s     <- tlvs update_add_htlc_known
  pk    <- record 0 tlv_point s
  pure (UpdateAddHtlc cid hid amt ph cltv onion pk
          (unknown update_add_htlc_known s))

-- update_fulfill_htlc --------------------------------------------------------

update_fulfill_htlc_known :: [Word64]
update_fulfill_htlc_known = [1, 3]

-- | Encode an t'UpdateFulfillHtlc' payload.
encode_update_fulfill_htlc
  :: UpdateFulfillHtlc -> Either EncodeError BS.ByteString
encode_update_fulfill_htlc m = do
  ts <- encode_tlvs update_fulfill_htlc_known
    (  opt 1 un_attribution_data (update_fulfill_htlc_attribution_data m)
    <> opt 3 id (update_fulfill_htlc_fulfillment_payload m) )
    (update_fulfill_htlc_tlvs m)
  let HtlcId hid = update_fulfill_htlc_id m
  pure $ mconcat [
      BOLT1.un_channel_id (update_fulfill_htlc_channel_id m)
    , BOLT1.encode_u64 hid
    , BOLT1.un_payment_preimage (update_fulfill_htlc_payment_preimage m)
    , ts
    ]

-- | Decode an t'UpdateFulfillHtlc' payload.
decode_update_fulfill_htlc
  :: BS.ByteString -> Either DecodeError UpdateFulfillHtlc
decode_update_fulfill_htlc = parse $ do
  cid <- channel_id
  hid <- htlc_id
  pre <- payment_preimage
  s   <- tlvs update_fulfill_htlc_known
  att <- record 1 attribution_data s
  pay <- record 3 Just s
  pure (UpdateFulfillHtlc cid hid pre att pay
          (unknown update_fulfill_htlc_known s))

-- update_fail_htlc -----------------------------------------------------------

update_fail_htlc_known :: [Word64]
update_fail_htlc_known = [1]

-- | Encode an t'UpdateFailHtlc' payload. Fails if @reason@ exceeds 65535
--   bytes.
encode_update_fail_htlc :: UpdateFailHtlc -> Either EncodeError BS.ByteString
encode_update_fail_htlc m = do
  reason <- u16_prefixed (update_fail_htlc_reason m)
  ts <- encode_tlvs update_fail_htlc_known
    (opt 1 un_attribution_data (update_fail_htlc_attribution_data m))
    (update_fail_htlc_tlvs m)
  let HtlcId hid = update_fail_htlc_id m
  pure $ mconcat [
      BOLT1.un_channel_id (update_fail_htlc_channel_id m)
    , BOLT1.encode_u64 hid
    , reason
    , ts
    ]

-- | Decode an t'UpdateFailHtlc' payload.
decode_update_fail_htlc :: BS.ByteString -> Either DecodeError UpdateFailHtlc
decode_update_fail_htlc = parse $ do
  cid    <- channel_id
  hid    <- htlc_id
  reason <- prefixed
  s      <- tlvs update_fail_htlc_known
  att    <- record 1 attribution_data s
  pure (UpdateFailHtlc cid hid reason att (unknown update_fail_htlc_known s))

-- update_fail_malformed_htlc -------------------------------------------------

-- | Encode an t'UpdateFailMalformedHtlc' payload.
encode_update_fail_malformed_htlc :: UpdateFailMalformedHtlc -> BS.ByteString
encode_update_fail_malformed_htlc
  (UpdateFailMalformedHtlc cid (HtlcId hid) oh code ts) = mconcat [
      BOLT1.un_channel_id cid
    , BOLT1.encode_u64 hid
    , un_onion_hash oh
    , BOLT1.encode_u16 code
    , BOLT1.encode_tlv_stream ts
    ]

-- | Decode an t'UpdateFailMalformedHtlc' payload.
decode_update_fail_malformed_htlc
  :: BS.ByteString -> Either DecodeError UpdateFailMalformedHtlc
decode_update_fail_malformed_htlc = parse $
  UpdateFailMalformedHtlc <$> channel_id <*> htlc_id <*> sized 32 onion_hash
                          <*> u16 <*> tlvs []

-- commitment_signed ----------------------------------------------------------

commitment_signed_known :: [Word64]
commitment_signed_known = [1]

-- | Encode a t'CommitmentSigned' payload. Fails if there are more than
--   65535 HTLC signatures.
encode_commitment_signed
  :: CommitmentSigned -> Either EncodeError BS.ByteString
encode_commitment_signed m = do
  let sigs = commitment_signed_htlc_signatures m
  n  <- count_prefix sigs
  ts <- encode_tlvs commitment_signed_known
    (opt 1 txid_bytes (commitment_signed_funding_txid m))
    (commitment_signed_tlvs m)
  pure $ mconcat [
      BOLT1.un_channel_id (commitment_signed_channel_id m)
    , BOLT1.un_signature (commitment_signed_signature m)
    , n
    , mconcat (map BOLT1.un_signature sigs)
    , ts
    ]

-- | Decode a t'CommitmentSigned' payload.
decode_commitment_signed
  :: BS.ByteString -> Either DecodeError CommitmentSigned
decode_commitment_signed = parse $ do
  cid  <- channel_id
  sig  <- signature
  n    <- u16
  sigs <- count (fromIntegral n) signature
  s    <- tlvs commitment_signed_known
  fund <- record 1 mk_txid s
  pure (CommitmentSigned cid sig sigs fund (unknown commitment_signed_known s))

-- revoke_and_ack, update_fee -------------------------------------------------

-- | Encode a t'RevokeAndAck' payload.
encode_revoke_and_ack :: RevokeAndAck -> BS.ByteString
encode_revoke_and_ack (RevokeAndAck cid sec pcp ts) = mconcat [
    BOLT1.un_channel_id cid
  , BOLT1.un_per_commitment_secret sec
  , BOLT1.un_point pcp
  , BOLT1.encode_tlv_stream ts
  ]

-- | Decode a t'RevokeAndAck' payload.
decode_revoke_and_ack :: BS.ByteString -> Either DecodeError RevokeAndAck
decode_revoke_and_ack = parse $
  RevokeAndAck <$> channel_id <*> per_commitment_secret <*> point <*> tlvs []

-- | Encode an t'UpdateFee' payload.
encode_update_fee :: UpdateFee -> BS.ByteString
encode_update_fee (UpdateFee cid fee ts) = mconcat [
    BOLT1.un_channel_id cid
  , BOLT1.encode_u32 fee
  , BOLT1.encode_tlv_stream ts
  ]

-- | Decode an t'UpdateFee' payload.
decode_update_fee :: BS.ByteString -> Either DecodeError UpdateFee
decode_update_fee = parse $ UpdateFee <$> channel_id <*> u32 <*> tlvs []

-- channel_reestablish --------------------------------------------------------

channel_reestablish_known :: [Word64]
channel_reestablish_known = [1]

-- | Encode a t'ChannelReestablish' payload.
encode_channel_reestablish
  :: ChannelReestablish -> Either EncodeError BS.ByteString
encode_channel_reestablish m = do
  ts <- encode_tlvs channel_reestablish_known
    (opt 1 next_funding_bytes (channel_reestablish_next_funding m))
    (channel_reestablish_tlvs m)
  pure $ mconcat [
      BOLT1.un_channel_id (channel_reestablish_channel_id m)
    , BOLT1.encode_u64 (channel_reestablish_next_commitment_number m)
    , BOLT1.encode_u64 (channel_reestablish_next_revocation_number m)
    , BOLT1.un_per_commitment_secret
        (channel_reestablish_your_last_per_commitment_secret m)
    , BOLT1.un_point (channel_reestablish_my_current_per_commitment_point m)
    , ts
    ]

-- | Decode a t'ChannelReestablish' payload.
decode_channel_reestablish
  :: BS.ByteString -> Either DecodeError ChannelReestablish
decode_channel_reestablish = parse $ do
  cid  <- channel_id
  nc   <- u64
  nr   <- u64
  sec  <- per_commitment_secret
  pcp  <- point
  s    <- tlvs channel_reestablish_known
  next <- record 1 tlv_next_funding s
  pure (ChannelReestablish cid nc nr sec pcp next
          (unknown channel_reestablish_known s))