Features.hs (5457B)
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 -- * Known feature 19 , KnownFeature(..) 20 , knownFeatureByBit 21 , knownFeatureByName 22 23 -- * Lookup 24 , featureByBit 25 , featureByName 26 27 -- * Known features table 28 , knownFeatures 29 ) where 30 31 import Control.DeepSeq (NFData) 32 import Data.IntMap.Strict (IntMap) 33 import qualified Data.IntMap.Strict as IM 34 import Data.Map.Strict (Map) 35 import qualified Data.Map.Strict as M 36 import Data.Word (Word16) 37 import GHC.Generics (Generic) 38 import Lightning.Protocol.BOLT9.Types (Context(..)) 39 40 -- | A known feature from the BOLT #9 specification. 41 data Feature = Feature { 42 featureName :: !String 43 -- ^ The canonical name of the feature. 44 , featureBaseBit :: {-# UNPACK #-} !Word16 45 -- ^ The even (compulsory) bit number; the odd (optional) bit is 46 -- @baseBit + 1@. 47 , featureContexts :: ![Context] 48 -- ^ Contexts in which this feature may be presented. 49 , featureDependencies :: ![String] 50 -- ^ Names of features this feature depends on. 51 , featureAssumed :: !Bool 52 -- ^ Whether this feature is assumed to be universally supported. 53 } 54 deriving (Eq, Show, Generic) 55 56 instance NFData Feature 57 58 -- | A feature that is known to be in the BOLT #9 specification. 59 -- 60 -- Constructed via 'knownFeatureByBit' or 'knownFeatureByName', 61 -- which validate that the feature exists in the known table. 62 newtype KnownFeature = KnownFeature { 63 unKnownFeature :: Feature 64 -- ^ Extract the underlying 'Feature'. 65 } 66 deriving (Eq, Show, Generic) 67 68 instance NFData KnownFeature 69 70 -- | Look up a known feature by bit number. 71 -- 72 -- Accepts either the even (compulsory) or odd (optional) bit. 73 -- 74 -- >>> fmap (featureName . unKnownFeature) (knownFeatureByBit 16) 75 -- Just "basic_mpp" 76 -- >>> knownFeatureByBit 999 77 -- Nothing 78 knownFeatureByBit :: Word16 -> Maybe KnownFeature 79 knownFeatureByBit !bit = fmap KnownFeature (featureByBit bit) 80 {-# INLINE knownFeatureByBit #-} 81 82 -- | Look up a known feature by its canonical name. 83 -- 84 -- >>> fmap (featureName . unKnownFeature) 85 -- ... (knownFeatureByName "basic_mpp") 86 -- Just "basic_mpp" 87 -- >>> knownFeatureByName "nonexistent" 88 -- Nothing 89 knownFeatureByName :: String -> Maybe KnownFeature 90 knownFeatureByName !name = 91 fmap KnownFeature (featureByName name) 92 {-# INLINE knownFeatureByName #-} 93 94 -- | The complete table of known features from BOLT #9. 95 knownFeatures :: [Feature] 96 knownFeatures = [ 97 Feature "option_data_loss_protect" 0 [] [] True 98 , Feature "option_upfront_shutdown_script" 4 [Init, NodeAnn] [] False 99 , Feature "gossip_queries" 6 [] [] False 100 , Feature "var_onion_optin" 8 [] [] True 101 , Feature "gossip_queries_ex" 10 [Init, NodeAnn] [] False 102 , Feature "option_static_remotekey" 12 [] [] True 103 , Feature "payment_secret" 14 [] [] True 104 , Feature "basic_mpp" 16 [Init, NodeAnn, Invoice] 105 ["payment_secret"] False 106 , Feature "option_support_large_channel" 18 [Init, NodeAnn] [] False 107 , Feature "option_anchors" 22 [Init, NodeAnn, ChanType] [] False 108 , Feature "option_route_blinding" 24 [Init, NodeAnn, Invoice] [] False 109 , Feature "option_shutdown_anysegwit" 26 [Init, NodeAnn] [] False 110 , Feature "option_dual_fund" 28 [Init, NodeAnn] [] False 111 , Feature "option_quiesce" 34 [Init, NodeAnn] [] False 112 , Feature "option_attribution_data" 36 [Init, NodeAnn, Invoice] 113 [] False 114 , Feature "option_onion_messages" 38 [Init, NodeAnn] [] False 115 , Feature "option_provide_storage" 42 [Init, NodeAnn] [] False 116 , Feature "option_channel_type" 44 [] [] True 117 , Feature "option_scid_alias" 46 [Init, NodeAnn, ChanType] [] False 118 , Feature "option_payment_metadata" 48 [Invoice] [] False 119 , Feature "option_zeroconf" 50 [Init, NodeAnn, ChanType] 120 ["option_scid_alias"] False 121 , Feature "option_simple_close" 60 [Init, NodeAnn] 122 ["option_shutdown_anysegwit"] False 123 ] 124 125 -- | Look up a feature by bit number. 126 -- 127 -- Accepts either the even (compulsory) or odd (optional) bit of the pair. 128 -- 129 -- >>> fmap featureName (featureByBit 16) 130 -- Just "basic_mpp" 131 -- >>> fmap featureName (featureByBit 17) -- odd bit also works 132 -- Just "basic_mpp" 133 -- >>> featureByBit 999 134 -- Nothing 135 featureByBit :: Word16 -> Maybe Feature 136 featureByBit !bit = 137 let !baseBit = fromIntegral bit - (fromIntegral bit `mod` 2) 138 in IM.lookup baseBit featuresByBit 139 {-# INLINE featureByBit #-} 140 141 -- | Look up a feature by its canonical name. 142 -- 143 -- >>> fmap featureBaseBit (featureByName "basic_mpp") 144 -- Just 16 145 -- >>> featureByName "nonexistent" 146 -- Nothing 147 featureByName :: String -> Maybe Feature 148 featureByName !name = M.lookup name featuresByName 149 {-# INLINE featureByName #-} 150 151 -- Lookup tables ------------------------------------------------------------- 152 153 -- | Features indexed by base bit (even bit number). 154 featuresByBit :: IntMap Feature 155 featuresByBit = IM.fromList 156 [(fromIntegral (featureBaseBit f), f) | f <- knownFeatures] 157 158 -- | Features indexed by canonical name. 159 featuresByName :: Map String Feature 160 featuresByName = M.fromList 161 [(featureName f, f) | f <- knownFeatures]