commit e64ee423c47e5cf259b4f80d8e9124e39f16f190
parent cbdf43150a9671a42cb301fd9741b76f93f27762
Author: Jared Tobin <jared@jtobin.io>
Date: Sun, 25 Jan 2026 15:55:31 +0400
Reconcile Context type between Types and Features modules
Features.hs now imports Context from Types.hs instead of defining its
own version. This ensures a single source of truth for context handling.
Also add .gitignore for build artifacts and worktrees.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Diffstat:
5 files changed, 198 insertions(+), 34 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1,6 @@
+dist-newstyle/
+impl-*/
+*.hi
+*.o
+*.dyn_hi
+*.dyn_o
diff --git a/lib/Lightning/Protocol/BOLT9.hs b/lib/Lightning/Protocol/BOLT9.hs
@@ -41,6 +41,13 @@ module Lightning.Protocol.BOLT9 (
, set
, clear
, member
+
+ -- * Known features
+ , Feature(..)
+ , featureByBit
+ , featureByName
+ , knownFeatures
) where
+import Lightning.Protocol.BOLT9.Features
import Lightning.Protocol.BOLT9.Types as FV
diff --git a/lib/Lightning/Protocol/BOLT9/Features.hs b/lib/Lightning/Protocol/BOLT9/Features.hs
@@ -12,11 +12,8 @@
-- [BOLT #9](https://github.com/lightning/bolts/blob/master/09-features.md).
module Lightning.Protocol.BOLT9.Features (
- -- * Context
- Context(..)
-
-- * Feature
- , Feature(..)
+ Feature(..)
-- * Lookup
, featureByBit
@@ -30,21 +27,7 @@ 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
+import Lightning.Protocol.BOLT9.Types (Context(..))
-- | A known feature from the BOLT #9 specification.
data Feature = Feature {
@@ -68,30 +51,30 @@ instance NFData Feature
knownFeatures :: [Feature]
knownFeatures = [
Feature "option_data_loss_protect" 0 [] [] True
- , Feature "option_upfront_shutdown_script" 4 [Init, NodeAnnouncement] [] False
+ , Feature "option_upfront_shutdown_script" 4 [Init, NodeAnn] [] False
, Feature "gossip_queries" 6 [] [] False
, Feature "var_onion_optin" 8 [] [] True
- , Feature "gossip_queries_ex" 10 [Init, NodeAnnouncement] [] False
+ , Feature "gossip_queries_ex" 10 [Init, NodeAnn] [] False
, Feature "option_static_remotekey" 12 [] [] True
, Feature "payment_secret" 14 [] [] True
- , Feature "basic_mpp" 16 [Init, NodeAnnouncement, Invoice]
+ , Feature "basic_mpp" 16 [Init, NodeAnn, 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]
+ , Feature "option_support_large_channel" 18 [Init, NodeAnn] [] False
+ , Feature "option_anchors" 22 [Init, NodeAnn, ChanType] [] False
+ , Feature "option_route_blinding" 24 [Init, NodeAnn, Invoice] [] False
+ , Feature "option_shutdown_anysegwit" 26 [Init, NodeAnn] [] False
+ , Feature "option_dual_fund" 28 [Init, NodeAnn] [] False
+ , Feature "option_quiesce" 34 [Init, NodeAnn] [] False
+ , Feature "option_attribution_data" 36 [Init, NodeAnn, Invoice]
[] False
- , Feature "option_onion_messages" 38 [Init, NodeAnnouncement] [] False
- , Feature "option_provide_storage" 42 [Init, NodeAnnouncement] [] False
+ , Feature "option_onion_messages" 38 [Init, NodeAnn] [] False
+ , Feature "option_provide_storage" 42 [Init, NodeAnn] [] False
, Feature "option_channel_type" 44 [] [] True
- , Feature "option_scid_alias" 46 [Init, NodeAnnouncement, ChannelType] [] False
+ , Feature "option_scid_alias" 46 [Init, NodeAnn, ChanType] [] False
, Feature "option_payment_metadata" 48 [Invoice] [] False
- , Feature "option_zeroconf" 50 [Init, NodeAnnouncement, ChannelType]
+ , Feature "option_zeroconf" 50 [Init, NodeAnn, ChanType]
["option_scid_alias"] False
- , Feature "option_simple_close" 60 [Init, NodeAnnouncement]
+ , Feature "option_simple_close" 60 [Init, NodeAnn]
["option_shutdown_anysegwit"] False
]
diff --git a/plans/ARCH1.md b/plans/ARCH1.md
@@ -0,0 +1,95 @@
+# ARCH1: High-level architecture for ppad-bolt9
+
+## Goals
+
+- Represent BOLT 9 feature bits with strong types and safe invariants.
+- Provide total parsing, validation, and construction of feature vectors.
+- Enforce dependencies, context restrictions, and optional/required pairing.
+- Keep the core API small and stable; push policy to explicit validators.
+
+## Core concepts
+
+- Feature bit: numbered bit (0-based) with optional/required pairing
+ (odd/optional, even/required).
+- Feature pair: optional bit N and required bit N-1 (for odd N).
+- Context: where a feature vector appears (init, node_announcement,
+ channel_announcement, invoice, blinded path, channel_type).
+- Feature vector: packed bitset with context-specific restrictions.
+
+## Module layout (proposed)
+
+- `Lightning.Protocol.BOLT9`
+ - Public API: feature types, parsing, rendering, validation helpers.
+ - Re-export safe constructors and lookup functions.
+
+- `Lightning.Protocol.BOLT9.Feature`
+ - Enumerates known features from 09-features.md.
+ - Tables: bit number, name, contexts, dependencies, assumed.
+ - Total lookup: bit -> known feature info (Maybe).
+
+- `Lightning.Protocol.BOLT9.Context`
+ - ADT for contexts: Init, NodeAnn, ChanAnn, Invoice, Blinded, ChanType.
+ - Helpers for context-specific rules (C-, C+ handling).
+
+- `Lightning.Protocol.BOLT9.Vector`
+ - Packed representation of feature bits (ByteString).
+ - Functions: empty, set, clear, member, union, normalize.
+ - Safe constructors for optional vs required bits.
+
+- `Lightning.Protocol.BOLT9.Validate`
+ - Validation of a vector against:
+ - context restrictions
+ - optional/required conflict
+ - dependency closure
+ - must-not-set unknown bits (if required by caller)
+ - Validation of remote vectors for unknown bits handling.
+
+## Data model notes
+
+- Use newtypes for bit indices and for optional vs required bits.
+- Encode odd/even constraints in constructors (smart constructors).
+- Store dependencies as feature pairs (logical dependencies) rather than
+ raw bit numbers, then expand to actual bits for validation.
+
+## Invariants to enforce
+
+- No vector may contain both optional and required bits for a feature.
+- Context restrictions are respected:
+ - Some bits only in Init/Node/Chan/Invoice/Blinded/ChanType.
+ - C-: only optional (odd bit) allowed in channel_announcement.
+ - C+: only required (even bit) allowed in channel_announcement.
+- Known dependencies are transitively satisfied.
+- Unknown bits may be allowed but must be treated as mandatory if
+ both optional and required are set (receiver rule).
+
+## API shape
+
+- Parsing and rendering:
+ - parseFeatureVector :: ByteString -> Either Error FeatureVector
+ - renderFeatureVector :: FeatureVector -> ByteString
+
+- Construction:
+ - emptyVector
+ - setOptional :: Feature -> FeatureVector -> FeatureVector
+ - setRequired :: Feature -> FeatureVector -> FeatureVector
+
+- Validation:
+ - validateLocal :: Context -> FeatureVector -> Either Error Validated
+ - validateRemote :: Context -> FeatureVector -> Either Error Validated
+
+## Validation policy
+
+- Local validation is strict: unknown bits are rejected by default.
+- Remote validation is configurable:
+ - accept unknown optional bits
+ - reject unknown required bits
+ - configurable fallback for unknown pairs
+
+## Testing strategy
+
+- Unit tests for all known features (bit numbers, contexts, deps).
+- Property tests:
+ - dependencies are closed under set operations
+ - no constructor can produce invalid parity
+ - render/parse roundtrip
+
diff --git a/plans/IMPL1.md b/plans/IMPL1.md
@@ -0,0 +1,73 @@
+# IMPL1: Implementation plan for ppad-bolt9
+
+## Scope
+
+Implement BOLT 9 feature flags with typed representation, parsing,
+validation, and tests driven by etc/09-features.md.
+
+## Step plan
+
+1) Baseline types and context model
+ - Add `Context` ADT and helpers for context rules (C-/C+).
+ - Add newtypes for `BitIndex`, `OptionalBit`, `RequiredBit` with
+ smart constructors enforcing odd/even parity.
+ - Define `FeatureVector` wrapper over strict ByteString with basic
+ operations (empty, set, clear, member).
+ - Independent: yes, can proceed in parallel with step 2.
+
+2) Known feature table
+ - Encode the known features from 09-features.md in a table with:
+ name, base bit, contexts, dependencies, assumed.
+ - Provide total lookup functions:
+ `featureByBit`, `featureByName`.
+ - Provide accessors for contexts and dependencies.
+ - Independent: yes, can proceed in parallel with step 1.
+
+3) Parsing and rendering
+ - Implement parse from ByteString into FeatureVector (bit order per
+ BOLT 1, least-significant bit is bit 0).
+ - Implement rendering back to ByteString preserving compact length
+ rules (trim leading zero bytes).
+ - Add total helpers to map between bits and features.
+ - Depends on steps 1-2.
+
+4) Validation engine
+ - Implement local validation:
+ - no optional+required pair set
+ - context restrictions (C-/C+ and allowed contexts)
+ - dependency closure (transitive)
+ - must set required bits for required features
+ - Implement remote validation:
+ - treat unknown optional bits as ignorable
+ - reject unknown required bits
+ - if both bits set, treat as required
+ - Provide error ADT with structured reasons.
+ - Depends on steps 1-3.
+
+5) Public API surface in Lightning.Protocol.BOLT9
+ - Expose safe constructors and validation helpers.
+ - Add Haddock examples for exported functions.
+ - Ensure module header and OPTIONS_HADDOCK prune.
+ - Depends on steps 1-4.
+
+6) Tests
+ - Unit tests for known features table and bit parity.
+ - Unit tests for context restrictions and dependency checks.
+ - Property tests for render/parse roundtrip and dependency closure.
+ - Depends on steps 1-4.
+
+7) Benchmarks
+ - Criterion benchmarks for parse/validate/render.
+ - Weigh benchmark for allocation profile.
+ - Define NFData instances as needed.
+ - Depends on steps 1-4.
+
+## Notes for subagents
+
+- Use strictness annotations and INLINE for small helpers.
+- Avoid partial functions; make pattern matches exhaustive.
+- Keep lines under 80 chars.
+- Do not add new external deps without approval.
+- If you introduce validation policy knobs, keep defaults conservative
+ (reject unknown required bits).
+