bolt2

Lightning peer protocol, per BOLT #2 (docs.ppad.tech/bolt2).
git clone git://git.ppad.tech/bolt2.git
Log | Files | Refs | README | LICENSE

Types.hs (14122B)


      1 {-# OPTIONS_HADDOCK prune #-}
      2 {-# LANGUAGE BangPatterns #-}
      3 {-# LANGUAGE DeriveGeneric #-}
      4 {-# LANGUAGE DerivingStrategies #-}
      5 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
      6 
      7 -- |
      8 -- Module: Lightning.Protocol.BOLT2.Types
      9 -- Copyright: (c) 2025 Jared Tobin
     10 -- License: MIT
     11 -- Maintainer: Jared Tobin <jared@ppad.tech>
     12 --
     13 -- Core types for BOLT #2 peer protocol.
     14 --
     15 -- This module provides newtypes for identifiers, amounts, hashes, and
     16 -- keys used in the Lightning Network peer protocol.
     17 
     18 module Lightning.Protocol.BOLT2.Types (
     19   -- * Identifiers
     20     ChannelId
     21   , channelId
     22   , unChannelId
     23 
     24   -- * Amounts
     25   , Satoshis(..)
     26   , MilliSatoshis(..)
     27   , satoshisToMsat
     28   , msatToSatoshis
     29 
     30   -- * Cryptographic types
     31   , Signature
     32   , signature
     33   , unSignature
     34   , Point
     35   , point
     36   , unPoint
     37   , PaymentHash
     38   , paymentHash
     39   , unPaymentHash
     40   , PaymentPreimage
     41   , paymentPreimage
     42   , unPaymentPreimage
     43   , Secret
     44   , secret
     45   , unSecret
     46 
     47   -- * Transaction types
     48   , TxId(..)
     49   , mkTxId
     50   , OutPoint(..)
     51   , ScriptPubKey
     52   , scriptPubKey
     53   , unScriptPubKey
     54 
     55   -- * Chain types
     56   , ChainHash
     57   , chainHash
     58   , unChainHash
     59   , ShortChannelId(..)
     60   , shortChannelId
     61   , scidBlockHeight
     62   , scidTxIndex
     63   , scidOutputIndex
     64 
     65   -- * Protocol types
     66   , FeatureBits
     67   , featureBits
     68   , unFeatureBits
     69   , OnionPacket
     70   , onionPacket
     71   , unOnionPacket
     72 
     73   -- * Constants
     74   , channelIdLen
     75   , signatureLen
     76   , pointLen
     77   , chainHashLen
     78   , shortChannelIdLen
     79   , paymentHashLen
     80   , paymentPreimageLen
     81   , onionPacketLen
     82   , secretLen
     83   ) where
     84 
     85 import Bitcoin.Prim.Tx (TxId(..), mkTxId, OutPoint(..))
     86 import Control.DeepSeq (NFData)
     87 import Data.Bits (unsafeShiftL, unsafeShiftR, (.&.), (.|.))
     88 import qualified Data.ByteString as BS
     89 import Data.Word (Word16, Word32, Word64)
     90 import GHC.Generics (Generic)
     91 
     92 -- constants -------------------------------------------------------------------
     93 
     94 -- | Length of a channel_id in bytes (32).
     95 channelIdLen :: Int
     96 channelIdLen = 32
     97 {-# INLINE channelIdLen #-}
     98 
     99 -- | Length of a signature in bytes (64, compact format).
    100 signatureLen :: Int
    101 signatureLen = 64
    102 {-# INLINE signatureLen #-}
    103 
    104 -- | Length of a compressed secp256k1 public key in bytes (33).
    105 pointLen :: Int
    106 pointLen = 33
    107 {-# INLINE pointLen #-}
    108 
    109 -- | Length of a chain hash in bytes (32).
    110 chainHashLen :: Int
    111 chainHashLen = 32
    112 {-# INLINE chainHashLen #-}
    113 
    114 -- | Length of a short_channel_id in bytes (8).
    115 shortChannelIdLen :: Int
    116 shortChannelIdLen = 8
    117 {-# INLINE shortChannelIdLen #-}
    118 
    119 -- | Length of a payment hash in bytes (32).
    120 paymentHashLen :: Int
    121 paymentHashLen = 32
    122 {-# INLINE paymentHashLen #-}
    123 
    124 -- | Length of a payment preimage in bytes (32).
    125 paymentPreimageLen :: Int
    126 paymentPreimageLen = 32
    127 {-# INLINE paymentPreimageLen #-}
    128 
    129 -- | Length of an onion routing packet in bytes (1366).
    130 onionPacketLen :: Int
    131 onionPacketLen = 1366
    132 {-# INLINE onionPacketLen #-}
    133 
    134 -- | Length of a per-commitment secret in bytes (32).
    135 secretLen :: Int
    136 secretLen = 32
    137 {-# INLINE secretLen #-}
    138 
    139 -- identifiers -----------------------------------------------------------------
    140 
    141 -- | A 32-byte channel identifier.
    142 --
    143 -- Derived from the funding transaction by XORing @funding_txid@ with
    144 -- @funding_output_index@ (big-endian, altering the last 2 bytes).
    145 --
    146 -- For v2 channels, derived as @SHA256(lesser-revocation-basepoint ||
    147 -- greater-revocation-basepoint)@.
    148 newtype ChannelId = ChannelId BS.ByteString
    149   deriving stock (Eq, Ord, Show, Generic)
    150   deriving newtype NFData
    151 
    152 -- | Construct a 'ChannelId' from a 32-byte 'BS.ByteString'.
    153 --
    154 -- Returns 'Nothing' if the input is not exactly 32 bytes.
    155 --
    156 -- >>> channelId (BS.replicate 32 0x00)
    157 -- Just (ChannelId ...)
    158 -- >>> channelId (BS.replicate 31 0x00)
    159 -- Nothing
    160 channelId :: BS.ByteString -> Maybe ChannelId
    161 channelId !bs
    162   | BS.length bs == channelIdLen = Just $! ChannelId bs
    163   | otherwise                    = Nothing
    164 {-# INLINABLE channelId #-}
    165 
    166 -- | Extract the underlying 'BS.ByteString' from a 'ChannelId'.
    167 unChannelId :: ChannelId -> BS.ByteString
    168 unChannelId (ChannelId bs) = bs
    169 {-# INLINE unChannelId #-}
    170 
    171 -- amounts ---------------------------------------------------------------------
    172 
    173 -- | Amount in satoshis (1/100,000,000 of a bitcoin).
    174 --
    175 -- Stored as a 'Word64'. Maximum valid value is 21,000,000 * 100,000,000
    176 -- = 2,100,000,000,000,000 satoshis.
    177 newtype Satoshis = Satoshis { unSatoshis :: Word64 }
    178   deriving stock (Eq, Ord, Show, Generic)
    179   deriving newtype (NFData, Num, Enum, Real, Integral)
    180 
    181 -- | Amount in millisatoshis (1/1000 of a satoshi).
    182 --
    183 -- Stored as a 'Word64'. Used for HTLC amounts and channel balances.
    184 newtype MilliSatoshis = MilliSatoshis { unMilliSatoshis :: Word64 }
    185   deriving stock (Eq, Ord, Show, Generic)
    186   deriving newtype (NFData, Num, Enum, Real, Integral)
    187 
    188 -- | Convert 'Satoshis' to 'MilliSatoshis'.
    189 --
    190 -- >>> satoshisToMsat (Satoshis 1)
    191 -- MilliSatoshis 1000
    192 satoshisToMsat :: Satoshis -> MilliSatoshis
    193 satoshisToMsat (Satoshis !s) = MilliSatoshis $! s * 1000
    194 {-# INLINE satoshisToMsat #-}
    195 
    196 -- | Convert 'MilliSatoshis' to 'Satoshis', rounding down.
    197 --
    198 -- >>> msatToSatoshis (MilliSatoshis 1500)
    199 -- Satoshis 1
    200 msatToSatoshis :: MilliSatoshis -> Satoshis
    201 msatToSatoshis (MilliSatoshis !m) = Satoshis $! m `div` 1000
    202 {-# INLINE msatToSatoshis #-}
    203 
    204 -- cryptographic types ---------------------------------------------------------
    205 
    206 -- | A 64-byte compact ECDSA signature.
    207 --
    208 -- Used for commitment transaction signatures, HTLC signatures, and
    209 -- closing transaction signatures.
    210 newtype Signature = Signature BS.ByteString
    211   deriving stock (Eq, Ord, Show, Generic)
    212   deriving newtype NFData
    213 
    214 -- | Construct a 'Signature' from a 64-byte 'BS.ByteString'.
    215 --
    216 -- Returns 'Nothing' if the input is not exactly 64 bytes.
    217 signature :: BS.ByteString -> Maybe Signature
    218 signature !bs
    219   | BS.length bs == signatureLen = Just $! Signature bs
    220   | otherwise                    = Nothing
    221 {-# INLINABLE signature #-}
    222 
    223 -- | Extract the underlying 'BS.ByteString' from a 'Signature'.
    224 unSignature :: Signature -> BS.ByteString
    225 unSignature (Signature bs) = bs
    226 {-# INLINE unSignature #-}
    227 
    228 -- | A 33-byte compressed secp256k1 public key.
    229 --
    230 -- Used for funding pubkeys, basepoints, and per-commitment points.
    231 newtype Point = Point BS.ByteString
    232   deriving stock (Eq, Ord, Show, Generic)
    233   deriving newtype NFData
    234 
    235 -- | Construct a 'Point' from a 33-byte 'BS.ByteString'.
    236 --
    237 -- Returns 'Nothing' if the input is not exactly 33 bytes.
    238 --
    239 -- Note: This only validates the length. Use secp256k1 libraries for
    240 -- full point validation.
    241 point :: BS.ByteString -> Maybe Point
    242 point !bs
    243   | BS.length bs == pointLen = Just $! Point bs
    244   | otherwise                = Nothing
    245 {-# INLINABLE point #-}
    246 
    247 -- | Extract the underlying 'BS.ByteString' from a 'Point'.
    248 unPoint :: Point -> BS.ByteString
    249 unPoint (Point bs) = bs
    250 {-# INLINE unPoint #-}
    251 
    252 -- | A 32-byte SHA256 payment hash.
    253 --
    254 -- Used to identify HTLCs. The preimage that hashes to this value is
    255 -- required to claim the HTLC.
    256 newtype PaymentHash = PaymentHash BS.ByteString
    257   deriving stock (Eq, Ord, Show, Generic)
    258   deriving newtype NFData
    259 
    260 -- | Construct a 'PaymentHash' from a 32-byte 'BS.ByteString'.
    261 --
    262 -- Returns 'Nothing' if the input is not exactly 32 bytes.
    263 paymentHash :: BS.ByteString -> Maybe PaymentHash
    264 paymentHash !bs
    265   | BS.length bs == paymentHashLen = Just $! PaymentHash bs
    266   | otherwise                      = Nothing
    267 {-# INLINABLE paymentHash #-}
    268 
    269 -- | Extract the underlying 'BS.ByteString' from a 'PaymentHash'.
    270 unPaymentHash :: PaymentHash -> BS.ByteString
    271 unPaymentHash (PaymentHash bs) = bs
    272 {-# INLINE unPaymentHash #-}
    273 
    274 -- | A 32-byte payment preimage.
    275 --
    276 -- The SHA256 hash of this value produces the corresponding 'PaymentHash'.
    277 -- Knowledge of the preimage allows claiming an HTLC.
    278 newtype PaymentPreimage = PaymentPreimage BS.ByteString
    279   deriving stock (Eq, Ord, Show, Generic)
    280   deriving newtype NFData
    281 
    282 -- | Construct a 'PaymentPreimage' from a 32-byte 'BS.ByteString'.
    283 --
    284 -- Returns 'Nothing' if the input is not exactly 32 bytes.
    285 paymentPreimage :: BS.ByteString -> Maybe PaymentPreimage
    286 paymentPreimage !bs
    287   | BS.length bs == paymentPreimageLen = Just $! PaymentPreimage bs
    288   | otherwise                          = Nothing
    289 {-# INLINABLE paymentPreimage #-}
    290 
    291 -- | Extract the underlying 'BS.ByteString' from a 'PaymentPreimage'.
    292 unPaymentPreimage :: PaymentPreimage -> BS.ByteString
    293 unPaymentPreimage (PaymentPreimage bs) = bs
    294 {-# INLINE unPaymentPreimage #-}
    295 
    296 -- | A 32-byte per-commitment secret.
    297 --
    298 -- Used in revoke_and_ack and channel_reestablish messages to revoke
    299 -- old commitment transactions.
    300 newtype Secret = Secret BS.ByteString
    301   deriving stock (Eq, Ord, Show, Generic)
    302   deriving newtype NFData
    303 
    304 -- | Construct a 'Secret' from a 32-byte 'BS.ByteString'.
    305 --
    306 -- Returns 'Nothing' if the input is not exactly 32 bytes.
    307 secret :: BS.ByteString -> Maybe Secret
    308 secret !bs
    309   | BS.length bs == secretLen = Just $! Secret bs
    310   | otherwise                 = Nothing
    311 {-# INLINABLE secret #-}
    312 
    313 -- | Extract the underlying 'BS.ByteString' from a 'Secret'.
    314 unSecret :: Secret -> BS.ByteString
    315 unSecret (Secret bs) = bs
    316 {-# INLINE unSecret #-}
    317 
    318 -- transaction types -----------------------------------------------------------
    319 
    320 -- | A script pubkey (output script).
    321 --
    322 -- Variable length; used in shutdown messages, closing transactions, etc.
    323 newtype ScriptPubKey = ScriptPubKey BS.ByteString
    324   deriving stock (Eq, Ord, Show, Generic)
    325   deriving newtype NFData
    326 
    327 -- | Construct a 'ScriptPubKey' from a 'BS.ByteString'.
    328 --
    329 -- Accepts any length; validation of script structure is left to higher
    330 -- layers.
    331 scriptPubKey :: BS.ByteString -> ScriptPubKey
    332 scriptPubKey = ScriptPubKey
    333 {-# INLINE scriptPubKey #-}
    334 
    335 -- | Extract the underlying 'BS.ByteString' from a 'ScriptPubKey'.
    336 unScriptPubKey :: ScriptPubKey -> BS.ByteString
    337 unScriptPubKey (ScriptPubKey bs) = bs
    338 {-# INLINE unScriptPubKey #-}
    339 
    340 -- chain types -----------------------------------------------------------------
    341 
    342 -- | A 32-byte chain hash.
    343 --
    344 -- Identifies the blockchain (typically the genesis block hash).
    345 -- Used in @open_channel@ to specify which chain the channel will reside on.
    346 newtype ChainHash = ChainHash BS.ByteString
    347   deriving stock (Eq, Ord, Show, Generic)
    348   deriving newtype NFData
    349 
    350 -- | Construct a 'ChainHash' from a 32-byte 'BS.ByteString'.
    351 --
    352 -- Returns 'Nothing' if the input is not exactly 32 bytes.
    353 chainHash :: BS.ByteString -> Maybe ChainHash
    354 chainHash !bs
    355   | BS.length bs == chainHashLen = Just $! ChainHash bs
    356   | otherwise                    = Nothing
    357 {-# INLINABLE chainHash #-}
    358 
    359 -- | Extract the underlying 'BS.ByteString' from a 'ChainHash'.
    360 unChainHash :: ChainHash -> BS.ByteString
    361 unChainHash (ChainHash bs) = bs
    362 {-# INLINE unChainHash #-}
    363 
    364 -- | A short channel identifier (8 bytes).
    365 --
    366 -- Encodes the block height (3 bytes), transaction index (3 bytes), and
    367 -- output index (2 bytes) of the funding transaction output.
    368 --
    369 -- This is a compact representation for referencing channels in gossip
    370 -- and routing.
    371 data ShortChannelId = ShortChannelId
    372   { scidBytes :: {-# UNPACK #-} !Word64
    373   }
    374   deriving stock (Eq, Ord, Show, Generic)
    375 
    376 instance NFData ShortChannelId
    377 
    378 -- | Construct a 'ShortChannelId' from block height, tx index, and
    379 -- output index.
    380 --
    381 -- Returns 'Nothing' if any component exceeds its maximum value:
    382 --
    383 -- * block height: max 16,777,215 (2^24 - 1)
    384 -- * tx index: max 16,777,215 (2^24 - 1)
    385 -- * output index: max 65,535 (2^16 - 1)
    386 --
    387 -- >>> shortChannelId 800000 1234 0
    388 -- Just (ShortChannelId ...)
    389 shortChannelId
    390   :: Word32  -- ^ Block height (24 bits max)
    391   -> Word32  -- ^ Transaction index (24 bits max)
    392   -> Word16  -- ^ Output index
    393   -> Maybe ShortChannelId
    394 shortChannelId !blockHeight !txIndex !outputIndex
    395   | blockHeight > 0xFFFFFF = Nothing
    396   | txIndex > 0xFFFFFF     = Nothing
    397   | otherwise              = Just $! ShortChannelId scid
    398   where
    399     !scid = (fromIntegral blockHeight `unsafeShiftL` 40)
    400         .|. (fromIntegral txIndex `unsafeShiftL` 16)
    401         .|. fromIntegral outputIndex
    402 {-# INLINABLE shortChannelId #-}
    403 
    404 -- | Extract the block height from a 'ShortChannelId'.
    405 scidBlockHeight :: ShortChannelId -> Word32
    406 scidBlockHeight (ShortChannelId !w) =
    407   fromIntegral $! (w `unsafeShiftR` 40) .&. 0xFFFFFF
    408 {-# INLINE scidBlockHeight #-}
    409 
    410 -- | Extract the transaction index from a 'ShortChannelId'.
    411 scidTxIndex :: ShortChannelId -> Word32
    412 scidTxIndex (ShortChannelId !w) =
    413   fromIntegral $! (w `unsafeShiftR` 16) .&. 0xFFFFFF
    414 {-# INLINE scidTxIndex #-}
    415 
    416 -- | Extract the output index from a 'ShortChannelId'.
    417 scidOutputIndex :: ShortChannelId -> Word16
    418 scidOutputIndex (ShortChannelId !w) = fromIntegral $! w .&. 0xFFFF
    419 {-# INLINE scidOutputIndex #-}
    420 
    421 -- protocol types --------------------------------------------------------------
    422 
    423 -- | Feature bits (variable length).
    424 --
    425 -- Encodes supported/required features. Even bits indicate required
    426 -- features; odd bits indicate optional features.
    427 newtype FeatureBits = FeatureBits BS.ByteString
    428   deriving stock (Eq, Ord, Show, Generic)
    429   deriving newtype NFData
    430 
    431 -- | Construct 'FeatureBits' from a 'BS.ByteString'.
    432 --
    433 -- Accepts any length; feature bit parsing is left to higher layers.
    434 featureBits :: BS.ByteString -> FeatureBits
    435 featureBits = FeatureBits
    436 {-# INLINE featureBits #-}
    437 
    438 -- | Extract the underlying 'BS.ByteString' from 'FeatureBits'.
    439 unFeatureBits :: FeatureBits -> BS.ByteString
    440 unFeatureBits (FeatureBits bs) = bs
    441 {-# INLINE unFeatureBits #-}
    442 
    443 -- | A 1366-byte onion routing packet.
    444 --
    445 -- Contains encrypted routing information for HTLC forwarding, as
    446 -- specified in BOLT #4.
    447 newtype OnionPacket = OnionPacket BS.ByteString
    448   deriving stock (Eq, Ord, Show, Generic)
    449   deriving newtype NFData
    450 
    451 -- | Construct an 'OnionPacket' from a 1366-byte 'BS.ByteString'.
    452 --
    453 -- Returns 'Nothing' if the input is not exactly 1366 bytes.
    454 onionPacket :: BS.ByteString -> Maybe OnionPacket
    455 onionPacket !bs
    456   | BS.length bs == onionPacketLen = Just $! OnionPacket bs
    457   | otherwise                      = Nothing
    458 {-# INLINABLE onionPacket #-}
    459 
    460 -- | Extract the underlying 'BS.ByteString' from an 'OnionPacket'.
    461 unOnionPacket :: OnionPacket -> BS.ByteString
    462 unOnionPacket (OnionPacket bs) = bs
    463 {-# INLINE unOnionPacket #-}