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))