Codec.hs (4815B)
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 , hasFeature 25 , isFeatureSet 26 , listFeatures 27 ) where 28 29 import Data.ByteString (ByteString) 30 import qualified Data.ByteString as BS 31 import Data.Word (Word16) 32 import Lightning.Protocol.BOLT9.Features 33 import Lightning.Protocol.BOLT9.Types 34 ( FeatureLevel(..) 35 , FeatureVector 36 , bitIndex 37 , clear 38 , fromByteString 39 , member 40 , set 41 , unFeatureVector 42 ) 43 44 -- Parsing and rendering ------------------------------------------------------ 45 46 -- | Parse a ByteString into a FeatureVector. 47 -- 48 -- Alias for 'fromByteString'. 49 parse :: ByteString -> FeatureVector 50 parse = fromByteString 51 {-# INLINE parse #-} 52 53 -- | Render a FeatureVector to a ByteString, trimming leading zero bytes 54 -- for compact encoding. 55 render :: FeatureVector -> ByteString 56 render = BS.dropWhile (== 0) . unFeatureVector 57 {-# INLINE render #-} 58 59 -- Bit operations ------------------------------------------------------------- 60 61 -- | Set a bit by raw index. 62 -- 63 -- >>> setBit 17 empty 64 -- FeatureVector {unFeatureVector = "\STX"} 65 setBit :: Word16 -> FeatureVector -> FeatureVector 66 setBit !idx = set (bitIndex idx) 67 {-# INLINE setBit #-} 68 69 -- | Clear a bit by raw index. 70 -- 71 -- >>> clearBit 17 (setBit 17 empty) 72 -- FeatureVector {unFeatureVector = ""} 73 clearBit :: Word16 -> FeatureVector -> FeatureVector 74 clearBit !idx = clear (bitIndex idx) 75 {-# INLINE clearBit #-} 76 77 -- | Test if a bit is set. 78 -- 79 -- >>> testBit 17 (setBit 17 empty) 80 -- True 81 -- >>> testBit 16 (setBit 17 empty) 82 -- False 83 testBit :: Word16 -> FeatureVector -> Bool 84 testBit !idx = member (bitIndex idx) 85 {-# INLINE testBit #-} 86 87 -- Feature operations --------------------------------------------------------- 88 89 -- | Set a feature's bit at the given level. 90 -- 91 -- 'Required' sets the even bit, 'Optional' sets the odd bit. 92 -- 93 -- >>> import Data.Maybe (fromJust) 94 -- >>> let mpp = fromJust (featureByName "basic_mpp") 95 -- >>> setFeature mpp Optional empty -- set optional bit (17) 96 -- FeatureVector {unFeatureVector = "\STX"} 97 -- >>> setFeature mpp Required empty -- set required bit (16) 98 -- FeatureVector {unFeatureVector = "\SOH"} 99 setFeature :: Feature -> FeatureLevel -> FeatureVector -> FeatureVector 100 setFeature !f !level = setBit targetBit 101 where 102 !baseBit = featureBaseBit f 103 !targetBit = case level of 104 Required -> baseBit 105 Optional -> baseBit + 1 106 {-# INLINE setFeature #-} 107 108 -- | Check if a feature is set in the vector. 109 -- 110 -- Returns: 111 -- 112 -- * @Just Required@ if the required (even) bit is set 113 -- * @Just Optional@ if the optional (odd) bit is set (and required is not) 114 -- * @Nothing@ if neither bit is set 115 -- 116 -- >>> import Data.Maybe (fromJust) 117 -- >>> let mpp = fromJust (featureByName "basic_mpp") 118 -- >>> hasFeature mpp (setFeature mpp Optional empty) 119 -- Just Optional 120 -- >>> hasFeature mpp (setFeature mpp Required empty) 121 -- Just Required 122 -- >>> hasFeature mpp empty 123 -- Nothing 124 hasFeature :: Feature -> FeatureVector -> Maybe FeatureLevel 125 hasFeature !f !fv 126 | testBit baseBit fv = Just Required 127 | testBit (baseBit + 1) fv = Just Optional 128 | otherwise = Nothing 129 where 130 !baseBit = featureBaseBit f 131 {-# INLINE hasFeature #-} 132 133 -- | Check if either bit of a feature is set in the vector. 134 -- 135 -- >>> import Data.Maybe (fromJust) 136 -- >>> let mpp = fromJust (featureByName "basic_mpp") 137 -- >>> isFeatureSet mpp (setFeature mpp Optional empty) 138 -- True 139 -- >>> isFeatureSet mpp empty 140 -- False 141 isFeatureSet :: Feature -> FeatureVector -> Bool 142 isFeatureSet !f !fv = 143 let !baseBit = featureBaseBit f 144 in testBit baseBit fv || testBit (baseBit + 1) fv 145 {-# INLINE isFeatureSet #-} 146 147 -- | List all known features that are set in the vector. 148 -- 149 -- Returns pairs of (Feature, FeatureLevel) indicating whether each 150 -- feature is set as required or optional. 151 -- 152 -- >>> import Data.Maybe (fromJust) 153 -- >>> let mpp = fromJust (featureByName "basic_mpp") 154 -- >>> let ps = fromJust (featureByName "payment_secret") 155 -- >>> let fv = setFeature mpp Optional (setFeature ps Required empty) 156 -- >>> map (\(f, l) -> (featureName f, l)) (listFeatures fv) 157 -- [("payment_secret",Required),("basic_mpp",Optional)] 158 listFeatures :: FeatureVector -> [(Feature, FeatureLevel)] 159 listFeatures !fv = foldr check [] knownFeatures 160 where 161 check !f !acc = case hasFeature f fv of 162 Just level -> (f, level) : acc 163 Nothing -> acc