packages feed

ppad-bolt4-0.0.1: lib/Lightning/Protocol/BOLT4/Types.hs

{-# OPTIONS_HADDOCK prune #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE PatternSynonyms #-}

-- |
-- Module: Lightning.Protocol.BOLT4.Types
-- Copyright: (c) 2025 Jared Tobin
-- License: MIT
-- Maintainer: Jared Tobin <jared@ppad.tech>
--
-- Core data types for BOLT4 onion routing.

module Lightning.Protocol.BOLT4.Types (
    -- * Packet types
    OnionPacket(..)
  , HopPayload(..)
  , ShortChannelId(..)
  , PaymentData(..)
  , TlvRecord(..)

    -- * Error types
  , FailureMessage(..)
  , FailureCode(..)
    -- ** Flag bits
  , pattern BADONION
  , pattern PERM
  , pattern NODE
  , pattern UPDATE
    -- ** Common failure codes
  , pattern InvalidRealm
  , pattern TemporaryNodeFailure
  , pattern PermanentNodeFailure
  , pattern RequiredNodeFeatureMissing
  , pattern InvalidOnionVersion
  , pattern InvalidOnionHmac
  , pattern InvalidOnionKey
  , pattern TemporaryChannelFailure
  , pattern PermanentChannelFailure
  , pattern AmountBelowMinimum
  , pattern FeeInsufficient
  , pattern IncorrectCltvExpiry
  , pattern ExpiryTooSoon
  , pattern IncorrectOrUnknownPaymentDetails
  , pattern FinalIncorrectCltvExpiry
  , pattern FinalIncorrectHtlcAmount
  , pattern ChannelDisabled
  , pattern ExpiryTooFar
  , pattern InvalidOnionPayload
  , pattern MppTimeout

    -- * Processing results
  , ProcessResult(..)
  , ForwardInfo(..)
  , ReceiveInfo(..)

    -- * Constants
  , onionPacketSize
  , hopPayloadsSize
  , hmacSize
  , pubkeySize
  , versionByte
  , maxPayloadSize
  ) where

import Data.Bits ((.&.), (.|.))
import qualified Data.ByteString as BS
import Data.Word (Word8, Word16, Word32, Word64)
import GHC.Generics (Generic)

-- Packet types -------------------------------------------------------------

-- | Complete onion packet (1366 bytes).
data OnionPacket = OnionPacket
  { opVersion      :: {-# UNPACK #-} !Word8
  , opEphemeralKey :: !BS.ByteString  -- ^ 33 bytes, compressed pubkey
  , opHopPayloads  :: !BS.ByteString  -- ^ 1300 bytes
  , opHmac         :: !BS.ByteString  -- ^ 32 bytes
  } deriving (Eq, Show, Generic)

-- | Parsed hop payload after decryption.
data HopPayload = HopPayload
  { hpAmtToForward   :: !(Maybe Word64)         -- ^ TLV type 2
  , hpOutgoingCltv   :: !(Maybe Word32)         -- ^ TLV type 4
  , hpShortChannelId :: !(Maybe ShortChannelId) -- ^ TLV type 6
  , hpPaymentData    :: !(Maybe PaymentData)    -- ^ TLV type 8
  , hpEncryptedData  :: !(Maybe BS.ByteString)  -- ^ TLV type 10
  , hpCurrentPathKey :: !(Maybe BS.ByteString)  -- ^ TLV type 12
  , hpUnknownTlvs    :: ![TlvRecord]            -- ^ Unknown types
  } deriving (Eq, Show, Generic)

-- | Short channel ID (8 bytes): block height, tx index, output index.
data ShortChannelId = ShortChannelId
  { sciBlockHeight :: {-# UNPACK #-} !Word32  -- ^ 3 bytes in encoding
  , sciTxIndex     :: {-# UNPACK #-} !Word32  -- ^ 3 bytes in encoding
  , sciOutputIndex :: {-# UNPACK #-} !Word16  -- ^ 2 bytes in encoding
  } deriving (Eq, Show, Generic)

-- | Payment data for final hop (TLV type 8).
data PaymentData = PaymentData
  { pdPaymentSecret :: !BS.ByteString         -- ^ 32 bytes
  , pdTotalMsat     :: {-# UNPACK #-} !Word64
  } deriving (Eq, Show, Generic)

-- | Generic TLV record for unknown/extension types.
data TlvRecord = TlvRecord
  { tlvType  :: {-# UNPACK #-} !Word64
  , tlvValue :: !BS.ByteString
  } deriving (Eq, Show, Generic)

-- Error types --------------------------------------------------------------

-- | Failure message from intermediate or final node.
data FailureMessage = FailureMessage
  { fmCode :: {-# UNPACK #-} !FailureCode
  , fmData :: !BS.ByteString
  , fmTlvs :: ![TlvRecord]
  } deriving (Eq, Show, Generic)

-- | 2-byte failure code with flag bits.
newtype FailureCode = FailureCode Word16
  deriving (Eq, Show)

-- Flag bits

-- | BADONION flag (0x8000): error was in parsing the onion.
pattern BADONION :: Word16
pattern BADONION = 0x8000

-- | PERM flag (0x4000): permanent failure, do not retry.
pattern PERM :: Word16
pattern PERM = 0x4000

-- | NODE flag (0x2000): node failure rather than channel.
pattern NODE :: Word16
pattern NODE = 0x2000

-- | UPDATE flag (0x1000): channel update is attached.
pattern UPDATE :: Word16
pattern UPDATE = 0x1000

-- Common failure codes

-- | Invalid realm byte in onion.
pattern InvalidRealm :: FailureCode
pattern InvalidRealm = FailureCode 0x4001  -- PERM .|. 1

-- | Temporary node failure.
pattern TemporaryNodeFailure :: FailureCode
pattern TemporaryNodeFailure = FailureCode 0x2002  -- NODE .|. 2

-- | Permanent node failure.
pattern PermanentNodeFailure :: FailureCode
pattern PermanentNodeFailure = FailureCode 0x6002  -- PERM .|. NODE .|. 2

-- | Required node feature missing.
pattern RequiredNodeFeatureMissing :: FailureCode
pattern RequiredNodeFeatureMissing = FailureCode 0x6003  -- PERM .|. NODE .|. 3

-- | Invalid onion version.
pattern InvalidOnionVersion :: FailureCode
pattern InvalidOnionVersion = FailureCode 0xC004  -- BADONION .|. PERM .|. 4

-- | Invalid HMAC in onion.
pattern InvalidOnionHmac :: FailureCode
pattern InvalidOnionHmac = FailureCode 0xC005  -- BADONION .|. PERM .|. 5

-- | Invalid ephemeral key in onion.
pattern InvalidOnionKey :: FailureCode
pattern InvalidOnionKey = FailureCode 0xC006  -- BADONION .|. PERM .|. 6

-- | Temporary channel failure.
pattern TemporaryChannelFailure :: FailureCode
pattern TemporaryChannelFailure = FailureCode 0x1007  -- UPDATE .|. 7

-- | Permanent channel failure.
pattern PermanentChannelFailure :: FailureCode
pattern PermanentChannelFailure = FailureCode 0x4008  -- PERM .|. 8

-- | Amount below minimum for channel.
pattern AmountBelowMinimum :: FailureCode
pattern AmountBelowMinimum = FailureCode 0x100B  -- UPDATE .|. 11

-- | Fee insufficient.
pattern FeeInsufficient :: FailureCode
pattern FeeInsufficient = FailureCode 0x100C  -- UPDATE .|. 12

-- | Incorrect CLTV expiry.
pattern IncorrectCltvExpiry :: FailureCode
pattern IncorrectCltvExpiry = FailureCode 0x100D  -- UPDATE .|. 13

-- | Expiry too soon.
pattern ExpiryTooSoon :: FailureCode
pattern ExpiryTooSoon = FailureCode 0x100E  -- UPDATE .|. 14

-- | Payment details incorrect or unknown.
pattern IncorrectOrUnknownPaymentDetails :: FailureCode
pattern IncorrectOrUnknownPaymentDetails = FailureCode 0x400F  -- PERM .|. 15

-- | Final incorrect CLTV expiry.
pattern FinalIncorrectCltvExpiry :: FailureCode
pattern FinalIncorrectCltvExpiry = FailureCode 18  -- 0x12

-- | Final incorrect HTLC amount.
pattern FinalIncorrectHtlcAmount :: FailureCode
pattern FinalIncorrectHtlcAmount = FailureCode 19  -- 0x13

-- | Channel disabled.
pattern ChannelDisabled :: FailureCode
pattern ChannelDisabled = FailureCode 0x1014  -- UPDATE .|. 20

-- | Expiry too far.
pattern ExpiryTooFar :: FailureCode
pattern ExpiryTooFar = FailureCode 21  -- 0x15

-- | Invalid onion payload.
pattern InvalidOnionPayload :: FailureCode
pattern InvalidOnionPayload = FailureCode 0x4016  -- PERM .|. 22

-- | MPP timeout.
pattern MppTimeout :: FailureCode
pattern MppTimeout = FailureCode 23  -- 0x17

-- Processing results -------------------------------------------------------

-- | Result of processing an onion packet.
data ProcessResult
  = Forward !ForwardInfo  -- ^ Forward to next hop
  | Receive !ReceiveInfo  -- ^ Final destination reached
  deriving (Eq, Show, Generic)

-- | Information for forwarding to next hop.
data ForwardInfo = ForwardInfo
  { fiNextPacket   :: !OnionPacket
  , fiPayload      :: !HopPayload
  , fiSharedSecret :: !BS.ByteString  -- ^ For error attribution
  } deriving (Eq, Show, Generic)

-- | Information for receiving at final destination.
data ReceiveInfo = ReceiveInfo
  { riPayload      :: !HopPayload
  , riSharedSecret :: !BS.ByteString
  } deriving (Eq, Show, Generic)

-- Constants ----------------------------------------------------------------

-- | Total onion packet size (1366 bytes).
onionPacketSize :: Int
onionPacketSize = 1366
{-# INLINE onionPacketSize #-}

-- | Hop payloads section size (1300 bytes).
hopPayloadsSize :: Int
hopPayloadsSize = 1300
{-# INLINE hopPayloadsSize #-}

-- | HMAC size (32 bytes).
hmacSize :: Int
hmacSize = 32
{-# INLINE hmacSize #-}

-- | Compressed public key size (33 bytes).
pubkeySize :: Int
pubkeySize = 33
{-# INLINE pubkeySize #-}

-- | Version byte for onion packets.
versionByte :: Word8
versionByte = 0x00
{-# INLINE versionByte #-}

-- | Maximum payload size (1300 - 32 - 1 = 1267 bytes).
maxPayloadSize :: Int
maxPayloadSize = hopPayloadsSize - hmacSize - 1
{-# INLINE maxPayloadSize #-}

-- Silence unused import warning
_useBits :: Word16
_useBits = BADONION .&. PERM .|. NODE .|. UPDATE