bolt7

Routing gossip protocol, per BOLT #7 (docs.ppad.tech/bolt7).
git clone git://git.ppad.tech/bolt7.git
Log | Files | Refs | README | LICENSE

Types.hs (12632B)


      1 {-# OPTIONS_HADDOCK prune #-}
      2 
      3 {-# LANGUAGE BangPatterns #-}
      4 {-# LANGUAGE DeriveGeneric #-}
      5 
      6 -- |
      7 -- Module: Lightning.Protocol.BOLT7.Types
      8 -- Copyright: (c) 2025 Jared Tobin
      9 -- License: MIT
     10 -- Maintainer: Jared Tobin <jared@ppad.tech>
     11 --
     12 -- Core types for BOLT #7 routing gossip.
     13 
     14 module Lightning.Protocol.BOLT7.Types (
     15   -- * Identifiers (re-exported from BOLT1)
     16     ChainHash(..)
     17   , chainHash
     18   , unChainHash
     19   , mainnetChainHash
     20   , ShortChannelId(..)
     21   , shortChannelId
     22   , scidBlockHeight
     23   , scidTxIndex
     24   , scidOutputIndex
     25   , scidWord64
     26   , scidFromBytes
     27   , scidToBytes
     28   , formatScid
     29   , ChannelId(..)
     30   , channelId
     31   , unChannelId
     32 
     33   -- * Cryptographic types (re-exported from BOLT1)
     34   , Signature(..)
     35   , signature
     36   , unSignature
     37   , Point(..)
     38   , point
     39   , unPoint
     40   , NodeId
     41   , nodeId
     42   , getNodeId
     43 
     44   -- * Node metadata
     45   , RgbColor
     46   , rgbColor
     47   , getRgbColor
     48   , Alias
     49   , alias
     50   , getAlias
     51   , Timestamp
     52   , FeatureBits
     53   , featureBits
     54   , getFeatureBits
     55 
     56   -- * Address types
     57   , Address(..)
     58   , IPv4Addr
     59   , ipv4Addr
     60   , getIPv4Addr
     61   , IPv6Addr
     62   , ipv6Addr
     63   , getIPv6Addr
     64   , TorV3Addr
     65   , torV3Addr
     66   , getTorV3Addr
     67   , Hostname
     68   , hostname
     69   , getHostname
     70 
     71   -- * Channel update flags
     72   , Direction(..)
     73   , ChannelStatus(..)
     74   , ChannelFlags(..)
     75   , encodeChannelFlags
     76   , decodeChannelFlags
     77 
     78   -- * Routing parameters
     79   , CltvExpiryDelta(..)
     80   , FeeBaseMsat(..)
     81   , FeeProportionalMillionths(..)
     82   , HtlcMinimumMsat(..)
     83   , HtlcMaximumMsat(..)
     84 
     85   -- * Block range types
     86   , BlockHeight(..)
     87   , BlockCount(..)
     88 
     89   -- * Constants
     90   , chainHashLen
     91   , shortChannelIdLen
     92   , channelIdLen
     93   , signatureLen
     94   , pointLen
     95   , nodeIdLen
     96   , rgbColorLen
     97   , aliasLen
     98   , ipv4AddrLen
     99   , ipv6AddrLen
    100   , torV3AddrLen
    101   ) where
    102 
    103 import Control.DeepSeq (NFData)
    104 import Data.Bits (shiftL, shiftR, (.&.), (.|.))
    105 import Data.ByteString (ByteString)
    106 import qualified Data.ByteString as BS
    107 import Data.Word (Word8, Word16, Word32, Word64)
    108 import GHC.Generics (Generic)
    109 import Lightning.Protocol.BOLT1.Prim
    110   ( ChainHash(..), unChainHash, chainHash
    111   , ShortChannelId(..), shortChannelId
    112   , scidBlockHeight, scidTxIndex, scidOutputIndex, scidWord64
    113   , ChannelId(..), unChannelId, channelId
    114   , Signature(..), unSignature, signature
    115   , Point(..), unPoint, point
    116   )
    117 
    118 -- Constants -------------------------------------------------------------------
    119 
    120 -- | Length of a chain hash (32 bytes).
    121 chainHashLen :: Int
    122 chainHashLen = 32
    123 {-# INLINE chainHashLen #-}
    124 
    125 -- | Length of a short channel ID (8 bytes).
    126 shortChannelIdLen :: Int
    127 shortChannelIdLen = 8
    128 {-# INLINE shortChannelIdLen #-}
    129 
    130 -- | Length of a channel ID (32 bytes).
    131 channelIdLen :: Int
    132 channelIdLen = 32
    133 {-# INLINE channelIdLen #-}
    134 
    135 -- | Length of a signature (64 bytes).
    136 signatureLen :: Int
    137 signatureLen = 64
    138 {-# INLINE signatureLen #-}
    139 
    140 -- | Length of a compressed public key (33 bytes).
    141 pointLen :: Int
    142 pointLen = 33
    143 {-# INLINE pointLen #-}
    144 
    145 -- | Length of a node ID (33 bytes, same as compressed public key).
    146 nodeIdLen :: Int
    147 nodeIdLen = 33
    148 {-# INLINE nodeIdLen #-}
    149 
    150 -- | Length of RGB color (3 bytes).
    151 rgbColorLen :: Int
    152 rgbColorLen = 3
    153 {-# INLINE rgbColorLen #-}
    154 
    155 -- | Length of node alias (32 bytes).
    156 aliasLen :: Int
    157 aliasLen = 32
    158 {-# INLINE aliasLen #-}
    159 
    160 -- | Length of IPv4 address (4 bytes).
    161 ipv4AddrLen :: Int
    162 ipv4AddrLen = 4
    163 {-# INLINE ipv4AddrLen #-}
    164 
    165 -- | Length of IPv6 address (16 bytes).
    166 ipv6AddrLen :: Int
    167 ipv6AddrLen = 16
    168 {-# INLINE ipv6AddrLen #-}
    169 
    170 -- | Length of Tor v3 address (35 bytes).
    171 torV3AddrLen :: Int
    172 torV3AddrLen = 35
    173 {-# INLINE torV3AddrLen #-}
    174 
    175 -- Identifiers -----------------------------------------------------------------
    176 
    177 -- | Bitcoin mainnet chain hash (genesis block hash, little-endian).
    178 mainnetChainHash :: ChainHash
    179 mainnetChainHash = ChainHash $ BS.pack
    180   [ 0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72
    181   , 0xc1, 0xa6, 0xa2, 0x46, 0xae, 0x63, 0xf7, 0x4f
    182   , 0x93, 0x1e, 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c
    183   , 0x68, 0xd6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00
    184   ]
    185 
    186 -- | Parse ShortChannelId from 8 big-endian bytes.
    187 scidFromBytes :: ByteString -> Maybe ShortChannelId
    188 scidFromBytes !bs
    189   | BS.length bs /= shortChannelIdLen = Nothing
    190   | otherwise =
    191       let !w = (fromIntegral (BS.index bs 0) `shiftL` 56)
    192            .|. (fromIntegral (BS.index bs 1) `shiftL` 48)
    193            .|. (fromIntegral (BS.index bs 2) `shiftL` 40)
    194            .|. (fromIntegral (BS.index bs 3) `shiftL` 32)
    195            .|. (fromIntegral (BS.index bs 4) `shiftL` 24)
    196            .|. (fromIntegral (BS.index bs 5) `shiftL` 16)
    197            .|. (fromIntegral (BS.index bs 6) `shiftL` 8)
    198            .|.  fromIntegral (BS.index bs 7) :: Word64
    199       in  Just (ShortChannelId w)
    200 {-# INLINE scidFromBytes #-}
    201 
    202 -- | Encode ShortChannelId as 8 big-endian bytes.
    203 scidToBytes :: ShortChannelId -> ByteString
    204 scidToBytes !sci =
    205   let !w = scidWord64 sci
    206   in  BS.pack
    207         [ fromIntegral (w `shiftR` 56)
    208         , fromIntegral (w `shiftR` 48)
    209         , fromIntegral (w `shiftR` 40)
    210         , fromIntegral (w `shiftR` 32)
    211         , fromIntegral (w `shiftR` 24)
    212         , fromIntegral (w `shiftR` 16)
    213         , fromIntegral (w `shiftR` 8)
    214         , fromIntegral w
    215         ]
    216 {-# INLINE scidToBytes #-}
    217 
    218 -- | Format short channel ID as human-readable string.
    219 formatScid :: ShortChannelId -> String
    220 formatScid sci =
    221   show (scidBlockHeight sci) ++ "x" ++
    222   show (scidTxIndex sci) ++ "x" ++
    223   show (scidOutputIndex sci)
    224 {-# INLINE formatScid #-}
    225 
    226 -- Cryptographic types ---------------------------------------------------------
    227 
    228 -- | Node ID (33 bytes, same as compressed public key).
    229 --
    230 -- Has Ord instance for lexicographic comparison (required by spec for
    231 -- channel announcements where node_id_1 < node_id_2).
    232 newtype NodeId = NodeId { getNodeId :: ByteString }
    233   deriving (Eq, Ord, Show, Generic)
    234 
    235 instance NFData NodeId
    236 
    237 -- | Smart constructor for NodeId. Returns Nothing if not 33 bytes.
    238 nodeId :: ByteString -> Maybe NodeId
    239 nodeId !bs
    240   | BS.length bs == nodeIdLen = Just (NodeId bs)
    241   | otherwise = Nothing
    242 {-# INLINE nodeId #-}
    243 
    244 -- Node metadata ---------------------------------------------------------------
    245 
    246 -- | RGB color (3 bytes).
    247 newtype RgbColor = RgbColor { getRgbColor :: ByteString }
    248   deriving (Eq, Show, Generic)
    249 
    250 instance NFData RgbColor
    251 
    252 -- | Smart constructor for RgbColor. Returns Nothing if not 3 bytes.
    253 rgbColor :: ByteString -> Maybe RgbColor
    254 rgbColor !bs
    255   | BS.length bs == rgbColorLen = Just (RgbColor bs)
    256   | otherwise = Nothing
    257 {-# INLINE rgbColor #-}
    258 
    259 -- | Node alias (32 bytes, UTF-8 padded with zero bytes).
    260 newtype Alias = Alias { getAlias :: ByteString }
    261   deriving (Eq, Show, Generic)
    262 
    263 instance NFData Alias
    264 
    265 -- | Smart constructor for Alias. Returns Nothing if not 32 bytes.
    266 alias :: ByteString -> Maybe Alias
    267 alias !bs
    268   | BS.length bs == aliasLen = Just (Alias bs)
    269   | otherwise = Nothing
    270 {-# INLINE alias #-}
    271 
    272 -- | Timestamp (Unix epoch seconds).
    273 type Timestamp = Word32
    274 
    275 -- | Feature bits (variable length).
    276 newtype FeatureBits = FeatureBits { getFeatureBits :: ByteString }
    277   deriving (Eq, Show, Generic)
    278 
    279 instance NFData FeatureBits
    280 
    281 -- | Smart constructor for FeatureBits (any length).
    282 featureBits :: ByteString -> FeatureBits
    283 featureBits = FeatureBits
    284 {-# INLINE featureBits #-}
    285 
    286 -- Address types ---------------------------------------------------------------
    287 
    288 -- | IPv4 address (4 bytes).
    289 newtype IPv4Addr = IPv4Addr { getIPv4Addr :: ByteString }
    290   deriving (Eq, Show, Generic)
    291 
    292 instance NFData IPv4Addr
    293 
    294 -- | Smart constructor for IPv4Addr. Returns Nothing if not 4 bytes.
    295 ipv4Addr :: ByteString -> Maybe IPv4Addr
    296 ipv4Addr !bs
    297   | BS.length bs == ipv4AddrLen = Just (IPv4Addr bs)
    298   | otherwise = Nothing
    299 {-# INLINE ipv4Addr #-}
    300 
    301 -- | IPv6 address (16 bytes).
    302 newtype IPv6Addr = IPv6Addr { getIPv6Addr :: ByteString }
    303   deriving (Eq, Show, Generic)
    304 
    305 instance NFData IPv6Addr
    306 
    307 -- | Smart constructor for IPv6Addr. Returns Nothing if not 16 bytes.
    308 ipv6Addr :: ByteString -> Maybe IPv6Addr
    309 ipv6Addr !bs
    310   | BS.length bs == ipv6AddrLen = Just (IPv6Addr bs)
    311   | otherwise = Nothing
    312 {-# INLINE ipv6Addr #-}
    313 
    314 -- | Tor v3 onion address (35 bytes: 32 pubkey + 2 checksum + 1 version).
    315 newtype TorV3Addr = TorV3Addr { getTorV3Addr :: ByteString }
    316   deriving (Eq, Show, Generic)
    317 
    318 instance NFData TorV3Addr
    319 
    320 -- | Smart constructor for TorV3Addr. Returns Nothing if not 35 bytes.
    321 torV3Addr :: ByteString -> Maybe TorV3Addr
    322 torV3Addr !bs
    323   | BS.length bs == torV3AddrLen = Just (TorV3Addr bs)
    324   | otherwise = Nothing
    325 {-# INLINE torV3Addr #-}
    326 
    327 -- | DNS hostname (1-255 bytes).
    328 --
    329 -- Per BOLT #7 address descriptor type 5, the hostname is
    330 -- a length-prefixed DNS name. The length byte limits it to
    331 -- 255 bytes.
    332 newtype Hostname = Hostname { getHostname :: ByteString }
    333   deriving (Eq, Show, Generic)
    334 
    335 instance NFData Hostname
    336 
    337 -- | Smart constructor for Hostname.
    338 --
    339 -- Returns Nothing if the hostname is empty or exceeds
    340 -- 255 bytes.
    341 hostname :: ByteString -> Maybe Hostname
    342 hostname !bs
    343   | BS.null bs = Nothing
    344   | BS.length bs > 255 = Nothing
    345   | otherwise = Just (Hostname bs)
    346 {-# INLINE hostname #-}
    347 
    348 -- | Network address with port.
    349 data Address
    350   = AddrIPv4  !IPv4Addr  !Word16  -- ^ IPv4 address + port
    351   | AddrIPv6  !IPv6Addr  !Word16  -- ^ IPv6 address + port
    352   | AddrTorV3 !TorV3Addr !Word16  -- ^ Tor v3 address + port
    353   | AddrDNS   !Hostname  !Word16  -- ^ DNS hostname + port
    354   deriving (Eq, Show, Generic)
    355 
    356 instance NFData Address
    357 
    358 -- Channel update flags --------------------------------------------------------
    359 
    360 -- | Direction of a channel_update.
    361 --
    362 -- Per BOLT #7, bit 0 of channel_flags indicates which node
    363 -- is the origin of the update.
    364 data Direction
    365   = NodeOne  -- ^ Update from node_id_1 (bit 0 = 0)
    366   | NodeTwo  -- ^ Update from node_id_2 (bit 0 = 1)
    367   deriving (Eq, Ord, Show, Generic)
    368 
    369 instance NFData Direction
    370 
    371 -- | Channel enabled\/disabled status.
    372 --
    373 -- Per BOLT #7, bit 1 of channel_flags indicates whether
    374 -- the channel is disabled.
    375 data ChannelStatus
    376   = Enabled   -- ^ Channel is active (bit 1 = 0)
    377   | Disabled  -- ^ Channel is disabled (bit 1 = 1)
    378   deriving (Eq, Ord, Show, Generic)
    379 
    380 instance NFData ChannelStatus
    381 
    382 -- | Channel flags for channel_update.
    383 --
    384 -- Bit 0: direction (0 = node_id_1, 1 = node_id_2).
    385 -- Bit 1: disabled (0 = enabled, 1 = disabled).
    386 data ChannelFlags = ChannelFlags
    387   { cfDirection :: !Direction      -- ^ Update origin
    388   , cfStatus    :: !ChannelStatus  -- ^ Channel status
    389   }
    390   deriving (Eq, Show, Generic)
    391 
    392 instance NFData ChannelFlags
    393 
    394 -- | Encode ChannelFlags to Word8.
    395 encodeChannelFlags :: ChannelFlags -> Word8
    396 encodeChannelFlags cf =
    397   dir .|. sta
    398   where
    399     dir = case cfDirection cf of
    400       NodeOne -> 0x00
    401       NodeTwo -> 0x01
    402     sta = case cfStatus cf of
    403       Enabled  -> 0x00
    404       Disabled -> 0x02
    405 {-# INLINE encodeChannelFlags #-}
    406 
    407 -- | Decode Word8 to ChannelFlags.
    408 decodeChannelFlags :: Word8 -> ChannelFlags
    409 decodeChannelFlags w = ChannelFlags
    410   { cfDirection = if w .&. 0x01 /= 0
    411       then NodeTwo
    412       else NodeOne
    413   , cfStatus = if w .&. 0x02 /= 0
    414       then Disabled
    415       else Enabled
    416   }
    417 {-# INLINE decodeChannelFlags #-}
    418 
    419 -- Routing parameters ----------------------------------------------------------
    420 
    421 -- | CLTV expiry delta.
    422 newtype CltvExpiryDelta = CltvExpiryDelta { getCltvExpiryDelta :: Word16 }
    423   deriving (Eq, Ord, Show, Generic)
    424 
    425 instance NFData CltvExpiryDelta
    426 
    427 -- | Base fee in millisatoshis.
    428 newtype FeeBaseMsat = FeeBaseMsat { getFeeBaseMsat :: Word32 }
    429   deriving (Eq, Ord, Show, Generic)
    430 
    431 instance NFData FeeBaseMsat
    432 
    433 -- | Proportional fee in millionths.
    434 newtype FeeProportionalMillionths = FeeProportionalMillionths
    435   { getFeeProportionalMillionths :: Word32 }
    436   deriving (Eq, Ord, Show, Generic)
    437 
    438 instance NFData FeeProportionalMillionths
    439 
    440 -- | Minimum HTLC value in millisatoshis.
    441 newtype HtlcMinimumMsat = HtlcMinimumMsat { getHtlcMinimumMsat :: Word64 }
    442   deriving (Eq, Ord, Show, Generic)
    443 
    444 instance NFData HtlcMinimumMsat
    445 
    446 -- | Maximum HTLC value in millisatoshis.
    447 newtype HtlcMaximumMsat = HtlcMaximumMsat
    448   { getHtlcMaximumMsat :: Word64 }
    449   deriving (Eq, Ord, Show, Generic)
    450 
    451 instance NFData HtlcMaximumMsat
    452 
    453 -- Block range types -----------------------------------------------------------
    454 
    455 -- | Absolute block height.
    456 --
    457 -- Used in query_channel_range and reply_channel_range for
    458 -- the first_blocknum field.
    459 newtype BlockHeight = BlockHeight
    460   { getBlockHeight :: Word32 }
    461   deriving (Eq, Ord, Show, Generic)
    462 
    463 instance NFData BlockHeight
    464 
    465 -- | Block count (relative duration).
    466 --
    467 -- Used in query_channel_range and reply_channel_range for
    468 -- the number_of_blocks field.
    469 newtype BlockCount = BlockCount
    470   { getBlockCount :: Word32 }
    471   deriving (Eq, Ord, Show, Generic)
    472 
    473 instance NFData BlockCount