Codec.hs (5697B)
1 {-# OPTIONS_HADDOCK prune #-} 2 {-# LANGUAGE BangPatterns #-} 3 4 -- | 5 -- Module: Lightning.Protocol.BOLT9.Codec 6 -- Copyright: (c) 2025 Jared Tobin 7 -- License: MIT 8 -- Maintainer: Jared Tobin <jared@ppad.tech> 9 -- 10 -- Parsing and rendering for BOLT #9 feature vectors. 11 12 module Lightning.Protocol.BOLT9.Codec ( 13 -- * Parsing and rendering 14 parse 15 , render 16 17 -- * Bit operations 18 , setBit 19 , clearBit 20 , testBit 21 22 -- * Feature operations 23 , setFeature 24 , setFeatureWithDeps 25 , hasFeature 26 , isFeatureSet 27 , listFeatures 28 ) where 29 30 import Data.ByteString (ByteString) 31 import qualified Data.ByteString as BS 32 import Data.Word (Word16) 33 import Lightning.Protocol.BOLT9.Features 34 import Lightning.Protocol.BOLT9.Types 35 ( FeatureLevel(..) 36 , FeatureVector 37 , bitIndex 38 , clear 39 , fromByteString 40 , member 41 , set 42 , unFeatureVector 43 ) 44 45 -- Parsing and rendering ------------------------------------------------------ 46 47 -- | Parse a ByteString into a FeatureVector. 48 -- 49 -- Alias for 'fromByteString'. 50 parse :: ByteString -> FeatureVector 51 parse = fromByteString 52 {-# INLINE parse #-} 53 54 -- | Render a FeatureVector to a ByteString, trimming leading zero bytes 55 -- for compact encoding. 56 render :: FeatureVector -> ByteString 57 render = BS.dropWhile (== 0) . unFeatureVector 58 {-# INLINE render #-} 59 60 -- Bit operations ------------------------------------------------------------- 61 62 -- | Set a bit by raw index. 63 -- 64 -- >>> setBit 17 empty 65 -- FeatureVector {unFeatureVector = "\STX"} 66 setBit :: Word16 -> FeatureVector -> FeatureVector 67 setBit !idx = set (bitIndex idx) 68 {-# INLINE setBit #-} 69 70 -- | Clear a bit by raw index. 71 -- 72 -- >>> clearBit 17 (setBit 17 empty) 73 -- FeatureVector {unFeatureVector = ""} 74 clearBit :: Word16 -> FeatureVector -> FeatureVector 75 clearBit !idx = clear (bitIndex idx) 76 {-# INLINE clearBit #-} 77 78 -- | Test if a bit is set. 79 -- 80 -- >>> testBit 17 (setBit 17 empty) 81 -- True 82 -- >>> testBit 16 (setBit 17 empty) 83 -- False 84 testBit :: Word16 -> FeatureVector -> Bool 85 testBit !idx = member (bitIndex idx) 86 {-# INLINE testBit #-} 87 88 -- Feature operations --------------------------------------------------------- 89 90 -- | Set a feature's bit at the given level. 91 -- 92 -- 'Required' sets the even bit, 'Optional' sets the odd bit. 93 -- 94 -- >>> import Data.Maybe (fromJust) 95 -- >>> let mpp = fromJust (featureByName "basic_mpp") 96 -- >>> setFeature mpp Optional empty -- set optional bit (17) 97 -- FeatureVector {unFeatureVector = "\STX"} 98 -- >>> setFeature mpp Required empty -- set required bit (16) 99 -- FeatureVector {unFeatureVector = "\SOH"} 100 setFeature :: Feature -> FeatureLevel -> FeatureVector -> FeatureVector 101 setFeature !f !level = setBit targetBit 102 where 103 !baseBit = featureBaseBit f 104 !targetBit = case level of 105 Required -> baseBit 106 Optional -> baseBit + 1 107 {-# INLINE setFeature #-} 108 109 -- | Set a feature and all its transitive dependencies. 110 -- 111 -- Dependencies are set at the same level as the feature itself. 112 -- Unknown dependencies (not in the known features table) are 113 -- silently skipped. 114 -- 115 -- >>> import Data.Maybe (fromJust) 116 -- >>> let mpp = fromJust (featureByName "basic_mpp") 117 -- >>> let fv = setFeatureWithDeps mpp Optional empty 118 -- >>> isFeatureSet mpp fv 119 -- True 120 -- >>> let Just ps = featureByName "payment_secret" 121 -- >>> isFeatureSet ps fv -- dependency auto-set 122 -- True 123 setFeatureWithDeps 124 :: Feature -> FeatureLevel -> FeatureVector -> FeatureVector 125 setFeatureWithDeps !f !level !fv = 126 let !fv' = setFeature f level fv 127 in foldr setDep fv' (featureDependencies f) 128 where 129 setDep !depName !acc = case featureByName depName of 130 Nothing -> acc 131 Just dep -> setFeatureWithDeps dep level acc 132 133 -- | Check if a feature is set in the vector. 134 -- 135 -- Returns: 136 -- 137 -- * @Just Required@ if the required (even) bit is set 138 -- * @Just Optional@ if the optional (odd) bit is set (and required is not) 139 -- * @Nothing@ if neither bit is set 140 -- 141 -- >>> import Data.Maybe (fromJust) 142 -- >>> let mpp = fromJust (featureByName "basic_mpp") 143 -- >>> hasFeature mpp (setFeature mpp Optional empty) 144 -- Just Optional 145 -- >>> hasFeature mpp (setFeature mpp Required empty) 146 -- Just Required 147 -- >>> hasFeature mpp empty 148 -- Nothing 149 hasFeature :: Feature -> FeatureVector -> Maybe FeatureLevel 150 hasFeature !f !fv 151 | testBit baseBit fv = Just Required 152 | testBit (baseBit + 1) fv = Just Optional 153 | otherwise = Nothing 154 where 155 !baseBit = featureBaseBit f 156 {-# INLINE hasFeature #-} 157 158 -- | Check if either bit of a feature is set in the vector. 159 -- 160 -- >>> import Data.Maybe (fromJust) 161 -- >>> let mpp = fromJust (featureByName "basic_mpp") 162 -- >>> isFeatureSet mpp (setFeature mpp Optional empty) 163 -- True 164 -- >>> isFeatureSet mpp empty 165 -- False 166 isFeatureSet :: Feature -> FeatureVector -> Bool 167 isFeatureSet !f !fv = 168 let !baseBit = featureBaseBit f 169 in testBit baseBit fv || testBit (baseBit + 1) fv 170 {-# INLINE isFeatureSet #-} 171 172 -- | List all known features that are set in the vector. 173 -- 174 -- Returns pairs of (Feature, FeatureLevel) indicating whether each 175 -- feature is set as required or optional. 176 -- 177 -- >>> import Data.Maybe (fromJust) 178 -- >>> let mpp = fromJust (featureByName "basic_mpp") 179 -- >>> let ps = fromJust (featureByName "payment_secret") 180 -- >>> let fv = setFeature mpp Optional (setFeature ps Required empty) 181 -- >>> map (\(f, l) -> (featureName f, l)) (listFeatures fv) 182 -- [("payment_secret",Required),("basic_mpp",Optional)] 183 listFeatures :: FeatureVector -> [(Feature, FeatureLevel)] 184 listFeatures !fv = foldr check [] knownFeatures 185 where 186 check !f !acc = case hasFeature f fv of 187 Just level -> (f, level) : acc 188 Nothing -> acc