bolt9

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

commit 94b8f712d025440cb5cf1c6e71db5ecb026b07c9
parent 564b7b53f6dfc7830c389793758fae9118c78b9f
Author: Jared Tobin <jared@jtobin.io>
Date:   Sun, 25 Jan 2026 15:52:59 +0400

Add known features table with lookup functions

Implements Step 2 of IMPL1.md: encode all known features from BOLT #9
specification in a structured table. Provides Feature record type with
name, base bit, contexts, dependencies, and assumed fields. Includes
featureByBit and featureByName lookup functions.

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

Diffstat:
Aflake.lock | 88+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib/Lightning/Protocol/BOLT9/Features.hs | 108+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mppad-bolt9.cabal | 1+
3 files changed, 197 insertions(+), 0 deletions(-)

diff --git a/flake.lock b/flake.lock @@ -0,0 +1,88 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1766840161, + "narHash": "sha256-Ss/LHpJJsng8vz1Pe33RSGIWUOcqM1fjrehjUkdrWio=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "3edc4a30ed3903fdf6f90c837f961fa6b49582d1", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "ppad-nixpkgs": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + }, + "locked": { + "lastModified": 1766932084, + "narHash": "sha256-GvVsbTfW+B7IQ9K/QP2xcXJAm1lhBin1jYZWNjOzT+o=", + "ref": "master", + "rev": "353e61763b959b960a55321a85423501e3e9ed7a", + "revCount": 2, + "type": "git", + "url": "git://git.ppad.tech/nixpkgs.git" + }, + "original": { + "ref": "master", + "type": "git", + "url": "git://git.ppad.tech/nixpkgs.git" + } + }, + "root": { + "inputs": { + "flake-utils": [ + "ppad-nixpkgs", + "flake-utils" + ], + "nixpkgs": [ + "ppad-nixpkgs", + "nixpkgs" + ], + "ppad-nixpkgs": "ppad-nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/lib/Lightning/Protocol/BOLT9/Features.hs b/lib/Lightning/Protocol/BOLT9/Features.hs @@ -0,0 +1,108 @@ +{-# OPTIONS_HADDOCK prune #-} +{-# LANGUAGE BangPatterns #-} +{-# LANGUAGE DeriveGeneric #-} + +-- | +-- Module: Lightning.Protocol.BOLT9.Features +-- Copyright: (c) 2025 Jared Tobin +-- License: MIT +-- Maintainer: Jared Tobin <jared@ppad.tech> +-- +-- Known feature table for the Lightning Network, per +-- [BOLT #9](https://github.com/lightning/bolts/blob/master/09-features.md). + +module Lightning.Protocol.BOLT9.Features ( + -- * Context + Context(..) + + -- * Feature + , Feature(..) + + -- * Lookup + , featureByBit + , featureByName + + -- * Known features table + , knownFeatures + ) where + +import Control.DeepSeq (NFData) +import Data.List (find) +import Data.Word (Word16) +import GHC.Generics (Generic) + +-- | Contexts in which a feature may be presented. +data Context + = Init -- ^ 'I': presented in the @init@ message. + | NodeAnnouncement -- ^ 'N': presented in @node_announcement@ messages. + | ChannelAnnouncement + -- ^ 'C': presented in @channel_announcement@ messages. + | Invoice -- ^ '9': presented in BOLT 11 invoices. + | BlindedPath -- ^ 'B': presented in @allowed_features@ of a + -- blinded path. + | ChannelType -- ^ 'T': used in @channel_type@ field when opening + -- channels. + deriving (Eq, Ord, Show, Generic) + +instance NFData Context + +-- | A known feature from the BOLT #9 specification. +data Feature = Feature { + featureName :: !String + -- ^ The canonical name of the feature. + , featureBaseBit :: {-# UNPACK #-} !Word16 + -- ^ The even (compulsory) bit number; the odd (optional) bit is + -- @baseBit + 1@. + , featureContexts :: ![Context] + -- ^ Contexts in which this feature may be presented. + , featureDependencies :: ![String] + -- ^ Names of features this feature depends on. + , featureAssumed :: !Bool + -- ^ Whether this feature is assumed to be universally supported. + } + deriving (Eq, Show, Generic) + +instance NFData Feature + +-- | The complete table of known features from BOLT #9. +knownFeatures :: [Feature] +knownFeatures = [ + Feature "option_data_loss_protect" 0 [] [] True + , Feature "option_upfront_shutdown_script" 4 [Init, NodeAnnouncement] [] False + , Feature "gossip_queries" 6 [] [] False + , Feature "var_onion_optin" 8 [] [] True + , Feature "gossip_queries_ex" 10 [Init, NodeAnnouncement] [] False + , Feature "option_static_remotekey" 12 [] [] True + , Feature "payment_secret" 14 [] [] True + , Feature "basic_mpp" 16 [Init, NodeAnnouncement, Invoice] + ["payment_secret"] False + , Feature "option_support_large_channel" 18 [Init, NodeAnnouncement] [] False + , Feature "option_anchors" 22 [Init, NodeAnnouncement, ChannelType] [] False + , Feature "option_route_blinding" 24 [Init, NodeAnnouncement, Invoice] [] False + , Feature "option_shutdown_anysegwit" 26 [Init, NodeAnnouncement] [] False + , Feature "option_dual_fund" 28 [Init, NodeAnnouncement] [] False + , Feature "option_quiesce" 34 [Init, NodeAnnouncement] [] False + , Feature "option_attribution_data" 36 [Init, NodeAnnouncement, Invoice] + [] False + , Feature "option_onion_messages" 38 [Init, NodeAnnouncement] [] False + , Feature "option_provide_storage" 42 [Init, NodeAnnouncement] [] False + , Feature "option_channel_type" 44 [] [] True + , Feature "option_scid_alias" 46 [Init, NodeAnnouncement, ChannelType] [] False + , Feature "option_payment_metadata" 48 [Invoice] [] False + , Feature "option_zeroconf" 50 [Init, NodeAnnouncement, ChannelType] + ["option_scid_alias"] False + , Feature "option_simple_close" 60 [Init, NodeAnnouncement] + ["option_shutdown_anysegwit"] False + ] + +-- | Look up a feature by bit number. +-- +-- Accepts either the even (compulsory) or odd (optional) bit of the pair. +featureByBit :: Word16 -> Maybe Feature +featureByBit !bit = + let baseBit = bit - (bit `mod` 2) -- round down to even + in find (\f -> featureBaseBit f == baseBit) knownFeatures + +-- | Look up a feature by its canonical name. +featureByName :: String -> Maybe Feature +featureByName !name = find (\f -> featureName f == name) knownFeatures diff --git a/ppad-bolt9.cabal b/ppad-bolt9.cabal @@ -25,6 +25,7 @@ library -Wall exposed-modules: Lightning.Protocol.BOLT9 + Lightning.Protocol.BOLT9.Features build-depends: base >= 4.9 && < 5 , bytestring >= 0.9 && < 0.13