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