Types.hs (13868B)
1 {-# OPTIONS_HADDOCK hide #-} 2 {-# LANGUAGE BangPatterns #-} 3 {-# LANGUAGE DeriveGeneric #-} 4 5 -- | 6 -- Module: Lightning.Protocol.BOLT7.Types 7 -- Copyright: (c) 2025 Jared Tobin 8 -- License: MIT 9 -- Maintainer: Jared Tobin <jared@ppad.tech> 10 -- 11 -- Field types for BOLT #7 gossip messages. 12 13 module Lightning.Protocol.BOLT7.Types ( 14 -- * Short channel ids 15 format_short_channel_id 16 17 -- * channel_update flags 18 , MessageFlags(..) 19 , Forwarding(..) 20 , message_flags 21 , message_flags_forwarding 22 , has_must_be_one 23 , ChannelFlags(..) 24 , Direction(..) 25 , ChannelStatus(..) 26 , channel_flags 27 , channel_flags_direction 28 , channel_flags_status 29 30 -- * node_announcement fields 31 , RgbColor(..) 32 , Alias(..) 33 , alias 34 , un_alias 35 , Address(..) 36 , IPv4Addr(..) 37 , ipv4_addr 38 , un_ipv4_addr 39 , IPv6Addr(..) 40 , ipv6_addr 41 , un_ipv6_addr 42 , TorV2Addr(..) 43 , tor_v2_addr 44 , un_tor_v2_addr 45 , TorV3Addr(..) 46 , tor_v3_addr 47 , un_tor_v3_addr 48 , Hostname(..) 49 , hostname 50 , un_hostname 51 , is_ascii_hostname 52 , usable_addresses 53 54 -- * Query fields 55 , QueryFlags(..) 56 , QueryFlag(..) 57 , query_flags 58 , has_query_flag 59 , QueryOption(..) 60 , QueryOptionFlag(..) 61 , query_option 62 , has_query_option 63 , ChannelUpdateTimestamps(..) 64 , ChannelUpdateChecksums(..) 65 ) where 66 67 import Control.DeepSeq (NFData) 68 import Data.Bits ((.|.), bit, testBit) 69 import qualified Data.ByteString as BS 70 import Data.Word (Word8, Word16, Word32, Word64) 71 import GHC.Generics (Generic) 72 import Lightning.Protocol.BOLT1 73 (ShortChannelId, scid_block_height, scid_tx_index, scid_output_index) 74 75 -- short channel ids ---------------------------------------------------------- 76 77 -- | Render a short channel id in the human-readable format of BOLT #7: 78 -- block height, transaction index and output index, in decimal, 79 -- separated by @x@. 80 -- 81 -- >>> fmap format_short_channel_id (short_channel_id 539268 845 1) 82 -- Just "539268x845x1" 83 format_short_channel_id :: ShortChannelId -> String 84 format_short_channel_id s = 85 show (scid_block_height s) ++ "x" ++ show (scid_tx_index s) ++ "x" 86 ++ show (scid_output_index s) 87 88 -- channel_update flags ------------------------------------------------------- 89 90 -- | The @message_flags@ byte of a @channel_update@: bit 0 is 91 -- @must_be_one@, bit 1 is @dont_forward@, and the other bits are 92 -- unassigned. Every byte is a valid value; unassigned bits are kept so 93 -- that re-encoding reproduces the signed bytes. 94 newtype MessageFlags = MessageFlags Word8 95 deriving (Eq, Show, Generic) 96 97 instance NFData MessageFlags 98 99 -- | Whether a @channel_update@ may be relayed to other peers (the 100 -- @dont_forward@ bit). 101 data Forwarding 102 = Forward -- ^ @dont_forward@ is 0 103 | DontForward -- ^ @dont_forward@ is 1, as for unannounced channels 104 deriving (Eq, Ord, Show, Generic) 105 106 instance NFData Forwarding 107 108 -- | The t'MessageFlags' an origin node sets: @must_be_one@ set, the given 109 -- @dont_forward@ bit, and no unassigned bits. 110 -- 111 -- >>> message_flags DontForward 112 -- MessageFlags 3 113 message_flags :: Forwarding -> MessageFlags 114 message_flags Forward = MessageFlags 0x01 115 message_flags DontForward = MessageFlags 0x03 116 {-# INLINE message_flags #-} 117 118 -- | The @dont_forward@ bit of t'MessageFlags'. 119 -- 120 -- >>> message_flags_forwarding (MessageFlags 0x01) 121 -- Forward 122 message_flags_forwarding :: MessageFlags -> Forwarding 123 message_flags_forwarding (MessageFlags w) 124 | testBit w 1 = DontForward 125 | otherwise = Forward 126 {-# INLINE message_flags_forwarding #-} 127 128 -- | Whether the @must_be_one@ bit is set. Senders must set it; receivers 129 -- ignore it. 130 -- 131 -- >>> has_must_be_one (MessageFlags 0x00) 132 -- False 133 has_must_be_one :: MessageFlags -> Bool 134 has_must_be_one (MessageFlags w) = testBit w 0 135 {-# INLINE has_must_be_one #-} 136 137 -- | The @channel_flags@ byte of a @channel_update@: bit 0 is 138 -- @direction@, bit 1 is @disable@, and the other bits are unassigned. 139 -- Every byte is a valid value; unassigned bits are kept so that 140 -- re-encoding reproduces the signed bytes. 141 newtype ChannelFlags = ChannelFlags Word8 142 deriving (Eq, Show, Generic) 143 144 instance NFData ChannelFlags 145 146 -- | The node a @channel_update@ originates from. 147 data Direction 148 = NodeOne -- ^ @node_id_1@ (@direction@ is 0) 149 | NodeTwo -- ^ @node_id_2@ (@direction@ is 1) 150 deriving (Eq, Ord, Show, Generic) 151 152 instance NFData Direction 153 154 -- | Whether a channel direction is usable. 155 data ChannelStatus 156 = Enabled -- ^ @disable@ is 0 157 | Disabled -- ^ @disable@ is 1 158 deriving (Eq, Ord, Show, Generic) 159 160 instance NFData ChannelStatus 161 162 -- | The t'ChannelFlags' for the given direction and status, with no 163 -- unassigned bits set. 164 -- 165 -- >>> channel_flags NodeTwo Disabled 166 -- ChannelFlags 3 167 channel_flags :: Direction -> ChannelStatus -> ChannelFlags 168 channel_flags d s = ChannelFlags (dir .|. sta) 169 where 170 dir = case d of 171 NodeOne -> 0x00 172 NodeTwo -> 0x01 173 sta = case s of 174 Enabled -> 0x00 175 Disabled -> 0x02 176 {-# INLINE channel_flags #-} 177 178 -- | The @direction@ bit of t'ChannelFlags'. 179 -- 180 -- >>> channel_flags_direction (ChannelFlags 0x05) 181 -- NodeTwo 182 channel_flags_direction :: ChannelFlags -> Direction 183 channel_flags_direction (ChannelFlags w) 184 | testBit w 0 = NodeTwo 185 | otherwise = NodeOne 186 {-# INLINE channel_flags_direction #-} 187 188 -- | The @disable@ bit of t'ChannelFlags'. 189 -- 190 -- >>> channel_flags_status (ChannelFlags 0x02) 191 -- Disabled 192 channel_flags_status :: ChannelFlags -> ChannelStatus 193 channel_flags_status (ChannelFlags w) 194 | testBit w 1 = Disabled 195 | otherwise = Enabled 196 {-# INLINE channel_flags_status #-} 197 198 -- node_announcement fields --------------------------------------------------- 199 200 -- | A node's colour: red, green and blue. 201 data RgbColor = RgbColor 202 {-# UNPACK #-} !Word8 203 {-# UNPACK #-} !Word8 204 {-# UNPACK #-} !Word8 205 deriving (Eq, Show, Generic) 206 207 instance NFData RgbColor 208 209 -- | A 32-byte node alias. Origin nodes should use UTF-8 padded with zero 210 -- bytes, but receivers must accept any bytes. 211 newtype Alias = Alias BS.ByteString 212 deriving (Eq, Show, Generic) 213 214 instance NFData Alias 215 216 -- | Construct an t'Alias' from exactly 32 bytes. 217 -- 218 -- >>> alias "too short" 219 -- Nothing 220 alias :: BS.ByteString -> Maybe Alias 221 alias bs 222 | BS.length bs == 32 = Just (Alias bs) 223 | otherwise = Nothing 224 {-# INLINE alias #-} 225 226 -- | The bytes of an t'Alias'. 227 un_alias :: Alias -> BS.ByteString 228 un_alias (Alias bs) = bs 229 {-# INLINE un_alias #-} 230 231 -- | A 4-byte IPv4 address. 232 newtype IPv4Addr = IPv4Addr BS.ByteString 233 deriving (Eq, Show, Generic) 234 235 instance NFData IPv4Addr 236 237 -- | Construct an t'IPv4Addr' from exactly 4 bytes. 238 -- 239 -- >>> ipv4_addr "\127\NUL\NUL\SOH" 240 -- Just (IPv4Addr "\DEL\NUL\NUL\SOH") 241 ipv4_addr :: BS.ByteString -> Maybe IPv4Addr 242 ipv4_addr bs 243 | BS.length bs == 4 = Just (IPv4Addr bs) 244 | otherwise = Nothing 245 {-# INLINE ipv4_addr #-} 246 247 -- | The bytes of an t'IPv4Addr'. 248 un_ipv4_addr :: IPv4Addr -> BS.ByteString 249 un_ipv4_addr (IPv4Addr bs) = bs 250 {-# INLINE un_ipv4_addr #-} 251 252 -- | A 16-byte IPv6 address. 253 newtype IPv6Addr = IPv6Addr BS.ByteString 254 deriving (Eq, Show, Generic) 255 256 instance NFData IPv6Addr 257 258 -- | Construct an t'IPv6Addr' from exactly 16 bytes. 259 ipv6_addr :: BS.ByteString -> Maybe IPv6Addr 260 ipv6_addr bs 261 | BS.length bs == 16 = Just (IPv6Addr bs) 262 | otherwise = Nothing 263 {-# INLINE ipv6_addr #-} 264 265 -- | The bytes of an t'IPv6Addr'. 266 un_ipv6_addr :: IPv6Addr -> BS.ByteString 267 un_ipv6_addr (IPv6Addr bs) = bs 268 {-# INLINE un_ipv6_addr #-} 269 270 -- | The 12 data bytes of a deprecated Tor v2 address descriptor (type 271 -- 3). They are kept, uninterpreted, so that re-encoding reproduces 272 -- the signed bytes. 273 newtype TorV2Addr = TorV2Addr BS.ByteString 274 deriving (Eq, Show, Generic) 275 276 instance NFData TorV2Addr 277 278 -- | Construct a t'TorV2Addr' from exactly 12 bytes. 279 tor_v2_addr :: BS.ByteString -> Maybe TorV2Addr 280 tor_v2_addr bs 281 | BS.length bs == 12 = Just (TorV2Addr bs) 282 | otherwise = Nothing 283 {-# INLINE tor_v2_addr #-} 284 285 -- | The bytes of a t'TorV2Addr'. 286 un_tor_v2_addr :: TorV2Addr -> BS.ByteString 287 un_tor_v2_addr (TorV2Addr bs) = bs 288 {-# INLINE un_tor_v2_addr #-} 289 290 -- | A 35-byte Tor v3 onion service address: a 32-byte ed25519 public 291 -- key, a 2-byte checksum and a 1-byte version. 292 newtype TorV3Addr = TorV3Addr BS.ByteString 293 deriving (Eq, Show, Generic) 294 295 instance NFData TorV3Addr 296 297 -- | Construct a t'TorV3Addr' from exactly 35 bytes. 298 tor_v3_addr :: BS.ByteString -> Maybe TorV3Addr 299 tor_v3_addr bs 300 | BS.length bs == 35 = Just (TorV3Addr bs) 301 | otherwise = Nothing 302 {-# INLINE tor_v3_addr #-} 303 304 -- | The bytes of a t'TorV3Addr'. 305 un_tor_v3_addr :: TorV3Addr -> BS.ByteString 306 un_tor_v3_addr (TorV3Addr bs) = bs 307 {-# INLINE un_tor_v3_addr #-} 308 309 -- | A DNS hostname of at most 255 bytes (the limit of its length byte). 310 -- 311 -- BOLT #7 requires hostnames to be ASCII, with other characters 312 -- Punycode-encoded, but receivers keep whatever bytes they are given; 313 -- see 'is_ascii_hostname'. 314 newtype Hostname = Hostname BS.ByteString 315 deriving (Eq, Show, Generic) 316 317 instance NFData Hostname 318 319 -- | Construct a t'Hostname' from at most 255 bytes. 320 -- 321 -- >>> hostname "ln.example.com" 322 -- Just (Hostname "ln.example.com") 323 -- >>> hostname (BS.replicate 256 0x61) 324 -- Nothing 325 hostname :: BS.ByteString -> Maybe Hostname 326 hostname bs 327 | BS.length bs <= 255 = Just (Hostname bs) 328 | otherwise = Nothing 329 {-# INLINE hostname #-} 330 331 -- | The bytes of a t'Hostname'. 332 un_hostname :: Hostname -> BS.ByteString 333 un_hostname (Hostname bs) = bs 334 {-# INLINE un_hostname #-} 335 336 -- | Whether a t'Hostname' is non-empty and ASCII, as BOLT #7 requires. 337 -- 338 -- >>> fmap is_ascii_hostname (hostname "caf\233") 339 -- Just False 340 is_ascii_hostname :: Hostname -> Bool 341 is_ascii_hostname (Hostname bs) = not (BS.null bs) && BS.all (< 0x80) bs 342 {-# INLINE is_ascii_hostname #-} 343 344 -- | An address descriptor of a known type, with its port where it has 345 -- one. 346 data Address 347 = AddrIPv4 !IPv4Addr {-# UNPACK #-} !Word16 -- ^ type 1 348 | AddrIPv6 !IPv6Addr {-# UNPACK #-} !Word16 -- ^ type 2 349 | AddrTorV2 !TorV2Addr -- ^ type 3 (deprecated) 350 | AddrTorV3 !TorV3Addr {-# UNPACK #-} !Word16 -- ^ type 4 351 | AddrDNS !Hostname {-# UNPACK #-} !Word16 -- ^ type 5 352 deriving (Eq, Show, Generic) 353 354 instance NFData Address 355 356 -- | The addresses a receiver should connect to, per the BOLT #7 receiver 357 -- rules: drops Tor v2 descriptors, descriptors with port 0, DNS 358 -- hostnames that are empty or not ASCII, and every DNS descriptor 359 -- after the first. 360 usable_addresses :: [Address] -> [Address] 361 usable_addresses = go False 362 where 363 go _ [] = [] 364 go !dns (a : as) = case a of 365 AddrIPv4 _ p -> port p 366 AddrIPv6 _ p -> port p 367 AddrTorV2 _ -> go dns as 368 AddrTorV3 _ p -> port p 369 AddrDNS h p 370 | dns -> go dns as 371 | p /= 0 && is_ascii_hostname h -> a : go True as 372 | otherwise -> go True as 373 where 374 port p 375 | p == 0 = go dns as 376 | otherwise = a : go dns as 377 378 -- query fields --------------------------------------------------------------- 379 380 -- | A @query_flags@ bitfield, sent for each short channel id in a 381 -- @query_short_channel_ids@. Every value is valid; unassigned bits are 382 -- kept. 383 newtype QueryFlags = QueryFlags Word64 384 deriving (Eq, Show, Generic) 385 386 instance NFData QueryFlags 387 388 -- | The assigned bits of t'QueryFlags'. 389 data QueryFlag 390 = WantChannelAnnouncement -- ^ bit 0 391 | WantChannelUpdate1 -- ^ bit 1: the @channel_update@ of node 1 392 | WantChannelUpdate2 -- ^ bit 2: the @channel_update@ of node 2 393 | WantNodeAnnouncement1 -- ^ bit 3: the @node_announcement@ of node 1 394 | WantNodeAnnouncement2 -- ^ bit 4: the @node_announcement@ of node 2 395 deriving (Eq, Ord, Show, Enum, Bounded, Generic) 396 397 instance NFData QueryFlag 398 399 -- | The t'QueryFlags' with exactly the given bits set. 400 -- 401 -- >>> query_flags [WantChannelAnnouncement, WantChannelUpdate2] 402 -- QueryFlags 5 403 query_flags :: [QueryFlag] -> QueryFlags 404 query_flags = QueryFlags . foldr (\f acc -> acc .|. bit (fromEnum f)) 0 405 406 -- | Whether the given bit is set. 407 -- 408 -- >>> has_query_flag WantChannelUpdate1 (QueryFlags 2) 409 -- True 410 has_query_flag :: QueryFlag -> QueryFlags -> Bool 411 has_query_flag f (QueryFlags w) = testBit w (fromEnum f) 412 {-# INLINE has_query_flag #-} 413 414 -- | The @query_option_flags@ bitfield of a @query_channel_range@. Every 415 -- value is valid; unassigned bits are kept. 416 newtype QueryOption = QueryOption Word64 417 deriving (Eq, Show, Generic) 418 419 instance NFData QueryOption 420 421 -- | The assigned bits of t'QueryOption'. 422 data QueryOptionFlag 423 = WantTimestamps -- ^ bit 0 424 | WantChecksums -- ^ bit 1 425 deriving (Eq, Ord, Show, Enum, Bounded, Generic) 426 427 instance NFData QueryOptionFlag 428 429 -- | The t'QueryOption' with exactly the given bits set. 430 -- 431 -- >>> query_option [WantTimestamps, WantChecksums] 432 -- QueryOption 3 433 query_option :: [QueryOptionFlag] -> QueryOption 434 query_option = QueryOption . foldr (\f acc -> acc .|. bit (fromEnum f)) 0 435 436 -- | Whether the given bit is set. 437 -- 438 -- >>> has_query_option WantChecksums (QueryOption 1) 439 -- False 440 has_query_option :: QueryOptionFlag -> QueryOption -> Bool 441 has_query_option f (QueryOption w) = testBit w (fromEnum f) 442 {-# INLINE has_query_option #-} 443 444 -- | The timestamps of a channel's latest @channel_update@s, from 445 -- @node_id_1@ and @node_id_2@ (0 where there is none). 446 data ChannelUpdateTimestamps = ChannelUpdateTimestamps 447 {-# UNPACK #-} !Word32 448 {-# UNPACK #-} !Word32 449 deriving (Eq, Show, Generic) 450 451 instance NFData ChannelUpdateTimestamps 452 453 -- | The checksums of a channel's latest @channel_update@s, from 454 -- @node_id_1@ and @node_id_2@ (0 where there is none). 455 data ChannelUpdateChecksums = ChannelUpdateChecksums 456 {-# UNPACK #-} !Word32 457 {-# UNPACK #-} !Word32 458 deriving (Eq, Show, Generic) 459 460 instance NFData ChannelUpdateChecksums