bolt9

Lightning feature flags, per BOLT #9 (docs.ppad.tech/bolt9).
git clone git://git.ppad.tech/bolt9.git
Log | Files | Refs | README | LICENSE

Features.hs (4222B)


      1 {-# OPTIONS_HADDOCK prune #-}
      2 {-# LANGUAGE BangPatterns #-}
      3 {-# LANGUAGE DeriveGeneric #-}
      4 
      5 -- |
      6 -- Module: Lightning.Protocol.BOLT9.Features
      7 -- Copyright: (c) 2025 Jared Tobin
      8 -- License: MIT
      9 -- Maintainer: Jared Tobin <jared@ppad.tech>
     10 --
     11 -- Known feature table for the Lightning Network, per
     12 -- [BOLT #9](https://github.com/lightning/bolts/blob/master/09-features.md).
     13 
     14 module Lightning.Protocol.BOLT9.Features (
     15     -- * Feature
     16     Feature(..)
     17 
     18     -- * Lookup
     19   , featureByBit
     20   , featureByName
     21 
     22     -- * Known features table
     23   , knownFeatures
     24   ) where
     25 
     26 import Control.DeepSeq (NFData)
     27 import Data.IntMap.Strict (IntMap)
     28 import qualified Data.IntMap.Strict as IM
     29 import Data.Map.Strict (Map)
     30 import qualified Data.Map.Strict as M
     31 import Data.Word (Word16)
     32 import GHC.Generics (Generic)
     33 import Lightning.Protocol.BOLT9.Types (Context(..))
     34 
     35 -- | A known feature from the BOLT #9 specification.
     36 data Feature = Feature {
     37     featureName         :: !String
     38     -- ^ The canonical name of the feature.
     39   , featureBaseBit      :: {-# UNPACK #-} !Word16
     40     -- ^ The even (compulsory) bit number; the odd (optional) bit is
     41     --   @baseBit + 1@.
     42   , featureContexts     :: ![Context]
     43     -- ^ Contexts in which this feature may be presented.
     44   , featureDependencies :: ![String]
     45     -- ^ Names of features this feature depends on.
     46   , featureAssumed      :: !Bool
     47     -- ^ Whether this feature is assumed to be universally supported.
     48   }
     49   deriving (Eq, Show, Generic)
     50 
     51 instance NFData Feature
     52 
     53 -- | The complete table of known features from BOLT #9.
     54 knownFeatures :: [Feature]
     55 knownFeatures = [
     56     Feature "option_data_loss_protect" 0 [] [] True
     57   , Feature "option_upfront_shutdown_script" 4 [Init, NodeAnn] [] False
     58   , Feature "gossip_queries" 6 [] [] False
     59   , Feature "var_onion_optin" 8 [] [] True
     60   , Feature "gossip_queries_ex" 10 [Init, NodeAnn] [] False
     61   , Feature "option_static_remotekey" 12 [] [] True
     62   , Feature "payment_secret" 14 [] [] True
     63   , Feature "basic_mpp" 16 [Init, NodeAnn, Invoice]
     64       ["payment_secret"] False
     65   , Feature "option_support_large_channel" 18 [Init, NodeAnn] [] False
     66   , Feature "option_anchors" 22 [Init, NodeAnn, ChanType] [] False
     67   , Feature "option_route_blinding" 24 [Init, NodeAnn, Invoice] [] False
     68   , Feature "option_shutdown_anysegwit" 26 [Init, NodeAnn] [] False
     69   , Feature "option_dual_fund" 28 [Init, NodeAnn] [] False
     70   , Feature "option_quiesce" 34 [Init, NodeAnn] [] False
     71   , Feature "option_attribution_data" 36 [Init, NodeAnn, Invoice]
     72       [] False
     73   , Feature "option_onion_messages" 38 [Init, NodeAnn] [] False
     74   , Feature "option_provide_storage" 42 [Init, NodeAnn] [] False
     75   , Feature "option_channel_type" 44 [] [] True
     76   , Feature "option_scid_alias" 46 [Init, NodeAnn, ChanType] [] False
     77   , Feature "option_payment_metadata" 48 [Invoice] [] False
     78   , Feature "option_zeroconf" 50 [Init, NodeAnn, ChanType]
     79       ["option_scid_alias"] False
     80   , Feature "option_simple_close" 60 [Init, NodeAnn]
     81       ["option_shutdown_anysegwit"] False
     82   ]
     83 
     84 -- | Look up a feature by bit number.
     85 --
     86 --   Accepts either the even (compulsory) or odd (optional) bit of the pair.
     87 --
     88 --   >>> fmap featureName (featureByBit 16)
     89 --   Just "basic_mpp"
     90 --   >>> fmap featureName (featureByBit 17)  -- odd bit also works
     91 --   Just "basic_mpp"
     92 --   >>> featureByBit 999
     93 --   Nothing
     94 featureByBit :: Word16 -> Maybe Feature
     95 featureByBit !bit =
     96   let !baseBit = fromIntegral bit - (fromIntegral bit `mod` 2)
     97   in  IM.lookup baseBit featuresByBit
     98 {-# INLINE featureByBit #-}
     99 
    100 -- | Look up a feature by its canonical name.
    101 --
    102 --   >>> fmap featureBaseBit (featureByName "basic_mpp")
    103 --   Just 16
    104 --   >>> featureByName "nonexistent"
    105 --   Nothing
    106 featureByName :: String -> Maybe Feature
    107 featureByName !name = M.lookup name featuresByName
    108 {-# INLINE featureByName #-}
    109 
    110 -- Lookup tables -------------------------------------------------------------
    111 
    112 -- | Features indexed by base bit (even bit number).
    113 featuresByBit :: IntMap Feature
    114 featuresByBit = IM.fromList
    115   [(fromIntegral (featureBaseBit f), f) | f <- knownFeatures]
    116 
    117 -- | Features indexed by canonical name.
    118 featuresByName :: Map String Feature
    119 featuresByName = M.fromList
    120   [(featureName f, f) | f <- knownFeatures]