bolt4

Onion routing protocol, per BOLT #4 (docs.ppad.tech/bolt4).
git clone git://git.ppad.tech/bolt4.git
Log | Files | Refs | README | LICENSE

Types.hs (10495B)


      1 {-# OPTIONS_HADDOCK prune #-}
      2 {-# LANGUAGE BangPatterns #-}
      3 {-# LANGUAGE DeriveGeneric #-}
      4 {-# LANGUAGE PatternSynonyms #-}
      5 
      6 -- |
      7 -- Module: Lightning.Protocol.BOLT4.Types
      8 -- Copyright: (c) 2025 Jared Tobin
      9 -- License: MIT
     10 -- Maintainer: Jared Tobin <jared@ppad.tech>
     11 --
     12 -- Core data types for BOLT4 onion routing.
     13 
     14 module Lightning.Protocol.BOLT4.Types (
     15     -- * Fixed-size newtypes
     16     Hmac32(..)
     17   , hmac32
     18   , unHmac32
     19   , HopPayloads(..)
     20   , hopPayloads
     21   , unHopPayloads
     22   , PaymentSecret(..)
     23   , paymentSecret
     24   , unPaymentSecret
     25 
     26     -- * Packet types
     27   , OnionPacket(..)
     28   , HopPayload(..)
     29   , ShortChannelId(..)
     30   , shortChannelId
     31   , scidBlockHeight
     32   , scidTxIndex
     33   , scidOutputIndex
     34   , scidWord64
     35   , PaymentData(..)
     36   , TlvRecord(..)
     37 
     38     -- * Error types
     39   , FailureMessage(..)
     40   , FailureCode(..)
     41     -- ** Flag bits
     42   , pattern BADONION
     43   , pattern PERM
     44   , pattern NODE
     45   , pattern UPDATE
     46     -- ** Common failure codes
     47   , pattern InvalidRealm
     48   , pattern TemporaryNodeFailure
     49   , pattern PermanentNodeFailure
     50   , pattern RequiredNodeFeatureMissing
     51   , pattern InvalidOnionVersion
     52   , pattern InvalidOnionHmac
     53   , pattern InvalidOnionKey
     54   , pattern TemporaryChannelFailure
     55   , pattern PermanentChannelFailure
     56   , pattern AmountBelowMinimum
     57   , pattern FeeInsufficient
     58   , pattern IncorrectCltvExpiry
     59   , pattern ExpiryTooSoon
     60   , pattern IncorrectOrUnknownPaymentDetails
     61   , pattern FinalIncorrectCltvExpiry
     62   , pattern FinalIncorrectHtlcAmount
     63   , pattern ChannelDisabled
     64   , pattern ExpiryTooFar
     65   , pattern InvalidOnionPayload
     66   , pattern MppTimeout
     67 
     68     -- * Processing results
     69   , ProcessResult(..)
     70   , ForwardInfo(..)
     71   , ReceiveInfo(..)
     72 
     73     -- * Constants
     74   , onionPacketSize
     75   , hopPayloadsSize
     76   , hmacSize
     77   , pubkeySize
     78   , versionByte
     79   , maxPayloadSize
     80   ) where
     81 
     82 import Data.Bits ((.&.), (.|.))
     83 import qualified Data.ByteString as BS
     84 import Data.Word (Word8, Word16, Word32, Word64)
     85 import GHC.Generics (Generic)
     86 import Lightning.Protocol.BOLT1.Prim
     87   ( ShortChannelId(..), shortChannelId
     88   , scidBlockHeight, scidTxIndex, scidOutputIndex, scidWord64
     89   )
     90 import Lightning.Protocol.BOLT4.Prim (SharedSecret)
     91 
     92 -- Fixed-size newtypes -------------------------------------------------------
     93 
     94 -- | 32-byte HMAC value.
     95 newtype Hmac32 = Hmac32 BS.ByteString
     96   deriving (Eq, Show, Generic)
     97 
     98 -- | Construct an 'Hmac32' from a 32-byte 'BS.ByteString'.
     99 --
    100 -- Returns 'Nothing' if the input is not exactly 32 bytes.
    101 hmac32 :: BS.ByteString -> Maybe Hmac32
    102 hmac32 !bs
    103   | BS.length bs == 32 = Just (Hmac32 bs)
    104   | otherwise = Nothing
    105 {-# INLINE hmac32 #-}
    106 
    107 -- | Extract the raw bytes from an 'Hmac32'.
    108 unHmac32 :: Hmac32 -> BS.ByteString
    109 unHmac32 (Hmac32 bs) = bs
    110 {-# INLINE unHmac32 #-}
    111 
    112 -- | 1300-byte hop payloads section.
    113 newtype HopPayloads = HopPayloads BS.ByteString
    114   deriving (Eq, Show, Generic)
    115 
    116 -- | Construct a 'HopPayloads' from a 1300-byte 'BS.ByteString'.
    117 --
    118 -- Returns 'Nothing' if the input is not exactly 1300 bytes.
    119 hopPayloads :: BS.ByteString -> Maybe HopPayloads
    120 hopPayloads !bs
    121   | BS.length bs == hopPayloadsSize = Just (HopPayloads bs)
    122   | otherwise = Nothing
    123 {-# INLINE hopPayloads #-}
    124 
    125 -- | Extract the raw bytes from 'HopPayloads'.
    126 unHopPayloads :: HopPayloads -> BS.ByteString
    127 unHopPayloads (HopPayloads bs) = bs
    128 {-# INLINE unHopPayloads #-}
    129 
    130 -- | 32-byte payment secret.
    131 newtype PaymentSecret = PaymentSecret BS.ByteString
    132   deriving (Eq, Show, Generic)
    133 
    134 -- | Construct a 'PaymentSecret' from a 32-byte 'BS.ByteString'.
    135 --
    136 -- Returns 'Nothing' if the input is not exactly 32 bytes.
    137 paymentSecret :: BS.ByteString -> Maybe PaymentSecret
    138 paymentSecret !bs
    139   | BS.length bs == 32 = Just (PaymentSecret bs)
    140   | otherwise = Nothing
    141 {-# INLINE paymentSecret #-}
    142 
    143 -- | Extract the raw bytes from a 'PaymentSecret'.
    144 unPaymentSecret :: PaymentSecret -> BS.ByteString
    145 unPaymentSecret (PaymentSecret bs) = bs
    146 {-# INLINE unPaymentSecret #-}
    147 
    148 -- Packet types -------------------------------------------------------------
    149 
    150 -- | Complete onion packet (1366 bytes).
    151 data OnionPacket = OnionPacket
    152   { opVersion      :: {-# UNPACK #-} !Word8
    153   , opEphemeralKey :: !BS.ByteString  -- ^ 33 bytes, compressed pubkey
    154   , opHopPayloads  :: !HopPayloads    -- ^ 1300 bytes
    155   , opHmac         :: !Hmac32         -- ^ 32 bytes
    156   } deriving (Eq, Show, Generic)
    157 
    158 -- | Parsed hop payload after decryption.
    159 data HopPayload = HopPayload
    160   { hpAmtToForward   :: !(Maybe Word64)         -- ^ TLV type 2
    161   , hpOutgoingCltv   :: !(Maybe Word32)         -- ^ TLV type 4
    162   , hpShortChannelId :: !(Maybe ShortChannelId) -- ^ TLV type 6
    163   , hpPaymentData    :: !(Maybe PaymentData)    -- ^ TLV type 8
    164   , hpEncryptedData  :: !(Maybe BS.ByteString)  -- ^ TLV type 10
    165   , hpCurrentPathKey :: !(Maybe BS.ByteString)  -- ^ TLV type 12
    166   , hpUnknownTlvs    :: ![TlvRecord]            -- ^ Unknown types
    167   } deriving (Eq, Show, Generic)
    168 
    169 -- | Payment data for final hop (TLV type 8).
    170 data PaymentData = PaymentData
    171   { pdPaymentSecret :: !PaymentSecret          -- ^ 32 bytes
    172   , pdTotalMsat     :: {-# UNPACK #-} !Word64
    173   } deriving (Eq, Show, Generic)
    174 
    175 -- | Generic TLV record for unknown/extension types.
    176 data TlvRecord = TlvRecord
    177   { tlvType  :: {-# UNPACK #-} !Word64
    178   , tlvValue :: !BS.ByteString
    179   } deriving (Eq, Show, Generic)
    180 
    181 -- Error types --------------------------------------------------------------
    182 
    183 -- | Failure message from intermediate or final node.
    184 data FailureMessage = FailureMessage
    185   { fmCode :: {-# UNPACK #-} !FailureCode
    186   , fmData :: !BS.ByteString
    187   , fmTlvs :: ![TlvRecord]
    188   } deriving (Eq, Show, Generic)
    189 
    190 -- | 2-byte failure code with flag bits.
    191 newtype FailureCode = FailureCode Word16
    192   deriving (Eq, Show)
    193 
    194 -- Flag bits
    195 
    196 -- | BADONION flag (0x8000): error was in parsing the onion.
    197 pattern BADONION :: Word16
    198 pattern BADONION = 0x8000
    199 
    200 -- | PERM flag (0x4000): permanent failure, do not retry.
    201 pattern PERM :: Word16
    202 pattern PERM = 0x4000
    203 
    204 -- | NODE flag (0x2000): node failure rather than channel.
    205 pattern NODE :: Word16
    206 pattern NODE = 0x2000
    207 
    208 -- | UPDATE flag (0x1000): channel update is attached.
    209 pattern UPDATE :: Word16
    210 pattern UPDATE = 0x1000
    211 
    212 -- Common failure codes
    213 
    214 -- | Invalid realm byte in onion.
    215 pattern InvalidRealm :: FailureCode
    216 pattern InvalidRealm = FailureCode 0x4001  -- PERM .|. 1
    217 
    218 -- | Temporary node failure.
    219 pattern TemporaryNodeFailure :: FailureCode
    220 pattern TemporaryNodeFailure = FailureCode 0x2002  -- NODE .|. 2
    221 
    222 -- | Permanent node failure.
    223 pattern PermanentNodeFailure :: FailureCode
    224 pattern PermanentNodeFailure = FailureCode 0x6002  -- PERM .|. NODE .|. 2
    225 
    226 -- | Required node feature missing.
    227 pattern RequiredNodeFeatureMissing :: FailureCode
    228 pattern RequiredNodeFeatureMissing = FailureCode 0x6003  -- PERM .|. NODE .|. 3
    229 
    230 -- | Invalid onion version.
    231 pattern InvalidOnionVersion :: FailureCode
    232 pattern InvalidOnionVersion = FailureCode 0xC004  -- BADONION .|. PERM .|. 4
    233 
    234 -- | Invalid HMAC in onion.
    235 pattern InvalidOnionHmac :: FailureCode
    236 pattern InvalidOnionHmac = FailureCode 0xC005  -- BADONION .|. PERM .|. 5
    237 
    238 -- | Invalid ephemeral key in onion.
    239 pattern InvalidOnionKey :: FailureCode
    240 pattern InvalidOnionKey = FailureCode 0xC006  -- BADONION .|. PERM .|. 6
    241 
    242 -- | Temporary channel failure.
    243 pattern TemporaryChannelFailure :: FailureCode
    244 pattern TemporaryChannelFailure = FailureCode 0x1007  -- UPDATE .|. 7
    245 
    246 -- | Permanent channel failure.
    247 pattern PermanentChannelFailure :: FailureCode
    248 pattern PermanentChannelFailure = FailureCode 0x4008  -- PERM .|. 8
    249 
    250 -- | Amount below minimum for channel.
    251 pattern AmountBelowMinimum :: FailureCode
    252 pattern AmountBelowMinimum = FailureCode 0x100B  -- UPDATE .|. 11
    253 
    254 -- | Fee insufficient.
    255 pattern FeeInsufficient :: FailureCode
    256 pattern FeeInsufficient = FailureCode 0x100C  -- UPDATE .|. 12
    257 
    258 -- | Incorrect CLTV expiry.
    259 pattern IncorrectCltvExpiry :: FailureCode
    260 pattern IncorrectCltvExpiry = FailureCode 0x100D  -- UPDATE .|. 13
    261 
    262 -- | Expiry too soon.
    263 pattern ExpiryTooSoon :: FailureCode
    264 pattern ExpiryTooSoon = FailureCode 0x100E  -- UPDATE .|. 14
    265 
    266 -- | Payment details incorrect or unknown.
    267 pattern IncorrectOrUnknownPaymentDetails :: FailureCode
    268 pattern IncorrectOrUnknownPaymentDetails = FailureCode 0x400F  -- PERM .|. 15
    269 
    270 -- | Final incorrect CLTV expiry.
    271 pattern FinalIncorrectCltvExpiry :: FailureCode
    272 pattern FinalIncorrectCltvExpiry = FailureCode 18  -- 0x12
    273 
    274 -- | Final incorrect HTLC amount.
    275 pattern FinalIncorrectHtlcAmount :: FailureCode
    276 pattern FinalIncorrectHtlcAmount = FailureCode 19  -- 0x13
    277 
    278 -- | Channel disabled.
    279 pattern ChannelDisabled :: FailureCode
    280 pattern ChannelDisabled = FailureCode 0x1014  -- UPDATE .|. 20
    281 
    282 -- | Expiry too far.
    283 pattern ExpiryTooFar :: FailureCode
    284 pattern ExpiryTooFar = FailureCode 21  -- 0x15
    285 
    286 -- | Invalid onion payload.
    287 pattern InvalidOnionPayload :: FailureCode
    288 pattern InvalidOnionPayload = FailureCode 0x4016  -- PERM .|. 22
    289 
    290 -- | MPP timeout.
    291 pattern MppTimeout :: FailureCode
    292 pattern MppTimeout = FailureCode 23  -- 0x17
    293 
    294 -- Processing results -------------------------------------------------------
    295 
    296 -- | Result of processing an onion packet.
    297 data ProcessResult
    298   = Forward !ForwardInfo  -- ^ Forward to next hop
    299   | Receive !ReceiveInfo  -- ^ Final destination reached
    300   deriving (Eq, Show, Generic)
    301 
    302 -- | Information for forwarding to next hop.
    303 data ForwardInfo = ForwardInfo
    304   { fiNextPacket   :: !OnionPacket
    305   , fiPayload      :: !HopPayload
    306   , fiSharedSecret :: !SharedSecret  -- ^ For error attribution
    307   } deriving (Eq, Show, Generic)
    308 
    309 -- | Information for receiving at final destination.
    310 data ReceiveInfo = ReceiveInfo
    311   { riPayload      :: !HopPayload
    312   , riSharedSecret :: !SharedSecret
    313   } deriving (Eq, Show, Generic)
    314 
    315 -- Constants ----------------------------------------------------------------
    316 
    317 -- | Total onion packet size (1366 bytes).
    318 onionPacketSize :: Int
    319 onionPacketSize = 1366
    320 {-# INLINE onionPacketSize #-}
    321 
    322 -- | Hop payloads section size (1300 bytes).
    323 hopPayloadsSize :: Int
    324 hopPayloadsSize = 1300
    325 {-# INLINE hopPayloadsSize #-}
    326 
    327 -- | HMAC size (32 bytes).
    328 hmacSize :: Int
    329 hmacSize = 32
    330 {-# INLINE hmacSize #-}
    331 
    332 -- | Compressed public key size (33 bytes).
    333 pubkeySize :: Int
    334 pubkeySize = 33
    335 {-# INLINE pubkeySize #-}
    336 
    337 -- | Version byte for onion packets.
    338 versionByte :: Word8
    339 versionByte = 0x00
    340 {-# INLINE versionByte #-}
    341 
    342 -- | Maximum payload size (1300 - 32 - 1 = 1267 bytes).
    343 maxPayloadSize :: Int
    344 maxPayloadSize = hopPayloadsSize - hmacSize - 1
    345 {-# INLINE maxPayloadSize #-}
    346 
    347 -- Silence unused import warning
    348 _useBits :: Word16
    349 _useBits = BADONION .&. PERM .|. NODE .|. UPDATE