bolt9

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

commit cecfa8c7a01795ebd39916a4c0b3e03e33b00c6c
parent 5931fdd136928ba8eb85af4a5a71579dc0a62189
Author: Jared Tobin <jared@jtobin.io>
Date:   Sun, 25 Jan 2026 16:08:21 +0400

Flesh out README with overview and usage examples

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

Diffstat:
MREADME.md | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 58 insertions(+), 2 deletions(-)

diff --git a/README.md b/README.md @@ -1,14 +1,70 @@ # ppad-bolt9 -A Haskell implementation of Lightning Network feature flags (BOLT #9). +A Haskell implementation of Lightning Network feature flags (BOLT #9), +providing type-safe feature vectors with validation and wire format +encoding. + +## Overview + +BOLT #9 defines feature flags that Lightning nodes advertise to indicate +support for optional protocol features. Features are represented as bit +positions in a variable-length bit vector: + +- **Even bits** (0, 2, 4, ...) indicate *required* support +- **Odd bits** (1, 3, 5, ...) indicate *optional* support + +For example, `basic_mpp` (multi-part payments) uses bits 16/17. A node +setting bit 16 requires peers to support it; bit 17 indicates optional +support. ## Usage A sample GHCi session: ``` + > :set -XOverloadedStrings > import Lightning.Protocol.BOLT9 - > -- TODO + > import Data.Maybe (fromJust) + > + > -- Look up features by name + > let mpp = fromJust $ featureByName "basic_mpp" + > featureBaseBit mpp + 16 + > featureDependencies mpp + ["payment_secret"] + > + > -- Build a feature vector with optional basic_mpp support + > let fv = setFeature mpp False empty + > hasFeature mpp fv + Just False + > + > -- Validation catches missing dependencies + > validateLocal Init fv + Left [MissingDependency "basic_mpp" "payment_secret"] + > + > -- Add the dependency + > let ps = fromJust $ featureByName "payment_secret" + > let fv' = setFeature ps False $ setFeature mpp False empty + > validateLocal Init fv' + Right () + > + > -- Render to wire format (compact, no leading zeros) + > render fv' + "\STX\128" + > + > -- Parse from wire format + > let fv'' = parse "\STX\128" + > listFeatures fv'' + [(Feature {featureName = "payment_secret", ...},False), + (Feature {featureName = "basic_mpp", ...},False)] + > + > -- Remote validation: unknown optional bits are OK + > validateRemote Init (setBit 201 empty) + Right () + > + > -- But unknown required bits are rejected + > validateRemote Init (setBit 200 empty) + Left [UnknownRequiredBit 200] ``` ## Documentation