packages feed

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

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

-- |
-- Module: Lightning.Protocol.BOLT2.Types
-- Copyright: (c) 2025 Jared Tobin
-- License: MIT
-- Maintainer: Jared Tobin <jared@ppad.tech>
--
-- Field types specific to BOLT #2.

module Lightning.Protocol.BOLT2.Types (
    HtlcId(..)
  , SerialId(..)
  , ScriptPubKey
  , script_pubkey
  , un_script_pubkey
  , Witness
  , witness
  , un_witness
  , OnionRoutingPacket
  , onion_routing_packet
  , un_onion_routing_packet
  , OnionHash
  , onion_hash
  , un_onion_hash
  , AttributionData
  , attribution_data
  , un_attribution_data
  , FeeRange(..)
  , NextFunding(..)
  ) where

import Bitcoin.Prim.Tx (TxId)
import Control.DeepSeq (NFData)
import qualified Data.ByteString as BS
import Data.Word (Word8, Word64)
import GHC.Generics (Generic)
import Lightning.Protocol.BOLT1 (Satoshi)

-- | An HTLC identifier (the @id@ field of the HTLC messages). The
--   offerer numbers its HTLCs from 0, incrementing by 1.
newtype HtlcId = HtlcId Word64
  deriving (Eq, Ord, Show, Generic)

instance NFData HtlcId

-- | The @serial_id@ of an input or output in interactive transaction
--   construction. The initiator sends even values, the non-initiator
--   odd ones.
newtype SerialId = SerialId Word64
  deriving (Eq, Ord, Show, Generic)

instance NFData SerialId

-- | A scriptPubKey, at most 65535 bytes (the most a u16 length prefix
--   can describe).
newtype ScriptPubKey = ScriptPubKey BS.ByteString
  deriving (Eq, Ord, Show, Generic)

instance NFData ScriptPubKey

-- | Construct a t'ScriptPubKey', failing above 65535 bytes. The script
--   itself isn't checked; the forms acceptable in @shutdown@ depend on
--   negotiated features.
--
--   >>> script_pubkey "\x51\x02\x4e\x73"
--   Just (ScriptPubKey "Q\STXNs")
--   >>> script_pubkey (BS.replicate 65536 0x00)
--   Nothing
script_pubkey :: BS.ByteString -> Maybe ScriptPubKey
script_pubkey bs
  | BS.length bs <= 65535 = Just (ScriptPubKey bs)
  | otherwise             = Nothing
{-# INLINE script_pubkey #-}

-- | The bytes of a t'ScriptPubKey'.
un_script_pubkey :: ScriptPubKey -> BS.ByteString
un_script_pubkey (ScriptPubKey bs) = bs
{-# INLINE un_script_pubkey #-}

-- | The serialized witness of one input in @tx_signatures@ (encoded as
--   in Bitcoin's wire protocol), at most 65535 bytes.
newtype Witness = Witness BS.ByteString
  deriving (Eq, Ord, Show, Generic)

instance NFData Witness

-- | Construct a t'Witness', failing above 65535 bytes. The contents
--   aren't checked.
--
--   >>> witness "\x01\x01\x2a"
--   Just (Witness "\SOH\SOH*")
witness :: BS.ByteString -> Maybe Witness
witness bs
  | BS.length bs <= 65535 = Just (Witness bs)
  | otherwise             = Nothing
{-# INLINE witness #-}

-- | The bytes of a t'Witness'.
un_witness :: Witness -> BS.ByteString
un_witness (Witness bs) = bs
{-# INLINE un_witness #-}

-- | The 1366-byte @onion_routing_packet@ of @update_add_htlc@, kept
--   opaque (BOLT #4 defines its structure).
newtype OnionRoutingPacket = OnionRoutingPacket BS.ByteString
  deriving (Eq, Ord, Show, Generic)

instance NFData OnionRoutingPacket

-- | Construct an t'OnionRoutingPacket' from exactly 1366 bytes.
--
--   >>> let bs = BS.replicate 1366 0x00
--   >>> fmap un_onion_routing_packet (onion_routing_packet bs) == Just bs
--   True
--   >>> onion_routing_packet (BS.take 1365 bs)
--   Nothing
onion_routing_packet :: BS.ByteString -> Maybe OnionRoutingPacket
onion_routing_packet bs
  | BS.length bs == 1366 = Just (OnionRoutingPacket bs)
  | otherwise            = Nothing
{-# INLINE onion_routing_packet #-}

-- | The bytes of an t'OnionRoutingPacket'.
un_onion_routing_packet :: OnionRoutingPacket -> BS.ByteString
un_onion_routing_packet (OnionRoutingPacket bs) = bs
{-# INLINE un_onion_routing_packet #-}

-- | The SHA256 hash of an onion routing packet (the @sha256_of_onion@
--   field of @update_fail_malformed_htlc@).
newtype OnionHash = OnionHash BS.ByteString
  deriving (Eq, Ord, Show, Generic)

instance NFData OnionHash

-- | Construct an t'OnionHash' from exactly 32 bytes.
--
--   >>> onion_hash (BS.replicate 31 0x00)
--   Nothing
onion_hash :: BS.ByteString -> Maybe OnionHash
onion_hash bs
  | BS.length bs == 32 = Just (OnionHash bs)
  | otherwise          = Nothing
{-# INLINE onion_hash #-}

-- | The bytes of an t'OnionHash'.
un_onion_hash :: OnionHash -> BS.ByteString
un_onion_hash (OnionHash bs) = bs
{-# INLINE un_onion_hash #-}

-- | The 920-byte @attribution_data@ of @update_fulfill_htlc@ and
--   @update_fail_htlc@: twenty u32 HTLC hold times followed by 210
--   four-byte truncated HMACs. Kept opaque (BOLT #4 defines its use).
newtype AttributionData = AttributionData BS.ByteString
  deriving (Eq, Ord, Show, Generic)

instance NFData AttributionData

-- | Construct an t'AttributionData' from exactly 920 bytes.
--
--   >>> let bs = BS.replicate 920 0x00
--   >>> fmap un_attribution_data (attribution_data bs) == Just bs
--   True
--   >>> attribution_data (BS.drop 1 bs)
--   Nothing
attribution_data :: BS.ByteString -> Maybe AttributionData
attribution_data bs
  | BS.length bs == 920 = Just (AttributionData bs)
  | otherwise           = Nothing
{-# INLINE attribution_data #-}

-- | The bytes of an t'AttributionData'.
un_attribution_data :: AttributionData -> BS.ByteString
un_attribution_data (AttributionData bs) = bs
{-# INLINE un_attribution_data #-}

-- | The @fee_range@ of @closing_signed@: the fees the sender is
--   prepared to pay for the closing transaction.
data FeeRange = FeeRange
  { fee_range_min_fee_satoshis :: {-# UNPACK #-} !Satoshi
  , fee_range_max_fee_satoshis :: {-# UNPACK #-} !Satoshi
  } deriving (Eq, Show, Generic)

instance NFData FeeRange

-- | The @next_funding@ record of @channel_reestablish@: an interactive
--   funding transaction whose signatures are still being exchanged.
data NextFunding = NextFunding
  { next_funding_txid             :: !TxId
  , next_funding_retransmit_flags :: {-# UNPACK #-} !Word8
    -- ^ bit 0: the sender wants @commitment_signed@ retransmitted
  } deriving (Eq, Show, Generic)

instance NFData NextFunding