bolt9

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

commit 4d4573133f9c4804c728b55773fa9ce4a04ae1dd
parent e64ee423c47e5cf259b4f80d8e9124e39f16f190
Author: Jared Tobin <jared@jtobin.io>
Date:   Sun, 25 Jan 2026 15:59:56 +0400

Merge impl/codec: parsing and rendering

Adds lib/Lightning/Protocol/BOLT9/Codec.hs with:
- parse/render for FeatureVector ByteString conversion
- setBit/clearBit/testBit convenience wrappers
- setFeature/hasFeature for feature-level operations
- listFeatures to enumerate set features

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

Diffstat:
Mlib/Lightning/Protocol/BOLT9.hs | 15+++++++++++++++
Alib/Lightning/Protocol/BOLT9/Codec.hs | 104+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mppad-bolt9.cabal | 1+
3 files changed, 120 insertions(+), 0 deletions(-)

diff --git a/lib/Lightning/Protocol/BOLT9.hs b/lib/Lightning/Protocol/BOLT9.hs @@ -47,7 +47,22 @@ module Lightning.Protocol.BOLT9 ( , featureByBit , featureByName , knownFeatures + + -- * Parsing and rendering + , parse + , render + + -- * Codec bit operations + , Codec.setBit + , Codec.clearBit + , Codec.testBit + + -- * Codec feature operations + , setFeature + , hasFeature + , listFeatures ) where +import Lightning.Protocol.BOLT9.Codec as Codec import Lightning.Protocol.BOLT9.Features import Lightning.Protocol.BOLT9.Types as FV diff --git a/lib/Lightning/Protocol/BOLT9/Codec.hs b/lib/Lightning/Protocol/BOLT9/Codec.hs @@ -0,0 +1,104 @@ +{-# OPTIONS_HADDOCK prune #-} +{-# LANGUAGE BangPatterns #-} + +-- | +-- Module: Lightning.Protocol.BOLT9.Codec +-- Copyright: (c) 2025 Jared Tobin +-- License: MIT +-- Maintainer: Jared Tobin <jared@ppad.tech> +-- +-- Parsing and rendering for BOLT #9 feature vectors. + +module Lightning.Protocol.BOLT9.Codec ( + -- * Parsing and rendering + parse + , render + + -- * Bit operations + , setBit + , clearBit + , testBit + + -- * Feature operations + , setFeature + , hasFeature + , listFeatures + ) where + +import Data.ByteString (ByteString) +import qualified Data.ByteString as BS +import Data.Word (Word16) +import Lightning.Protocol.BOLT9.Features +import Lightning.Protocol.BOLT9.Types + +-- Parsing and rendering ------------------------------------------------------ + +-- | Parse a ByteString into a FeatureVector. +-- +-- Alias for 'fromByteString'. +parse :: ByteString -> FeatureVector +parse = fromByteString +{-# INLINE parse #-} + +-- | Render a FeatureVector to a ByteString, trimming leading zero bytes +-- for compact encoding. +render :: FeatureVector -> ByteString +render = BS.dropWhile (== 0) . unFeatureVector +{-# INLINE render #-} + +-- Bit operations ------------------------------------------------------------- + +-- | Set a bit by raw index. +setBit :: Word16 -> FeatureVector -> FeatureVector +setBit !idx = set (bitIndex idx) +{-# INLINE setBit #-} + +-- | Clear a bit by raw index. +clearBit :: Word16 -> FeatureVector -> FeatureVector +clearBit !idx = clear (bitIndex idx) +{-# INLINE clearBit #-} + +-- | Test if a bit is set. +testBit :: Word16 -> FeatureVector -> Bool +testBit !idx = member (bitIndex idx) +{-# INLINE testBit #-} + +-- Feature operations --------------------------------------------------------- + +-- | Set or clear a feature's bit. +-- +-- If the Bool is True, sets the required (even) bit. +-- If the Bool is False, sets the optional (odd) bit. +setFeature :: Feature -> Bool -> FeatureVector -> FeatureVector +setFeature !f !required = setBit targetBit + where + !baseBit = featureBaseBit f + !targetBit = if required then baseBit else baseBit + 1 +{-# INLINE setFeature #-} + +-- | Check if a feature is set in the vector. +-- +-- Returns: +-- +-- * @Just True@ if the required (even) bit is set +-- * @Just False@ if the optional (odd) bit is set (and required is not) +-- * @Nothing@ if neither bit is set +hasFeature :: Feature -> FeatureVector -> Maybe Bool +hasFeature !f !fv + | testBit baseBit fv = Just True -- required + | testBit (baseBit + 1) fv = Just False -- optional + | otherwise = Nothing + where + !baseBit = featureBaseBit f +{-# INLINE hasFeature #-} + +-- | List all known features that are set in the vector. +-- +-- Returns pairs of (Feature, Bool) where the Bool indicates if the +-- required (even) bit is set (True) or the optional (odd) bit (False). +listFeatures :: FeatureVector -> [(Feature, Bool)] +listFeatures !fv = foldr check [] knownFeatures + where + check !f !acc = case hasFeature f fv of + Just isReq -> (f, isReq) : acc + Nothing -> acc diff --git a/ppad-bolt9.cabal b/ppad-bolt9.cabal @@ -25,6 +25,7 @@ library -Wall exposed-modules: Lightning.Protocol.BOLT9 + Lightning.Protocol.BOLT9.Codec Lightning.Protocol.BOLT9.Features Lightning.Protocol.BOLT9.Types build-depends: