bolt1

Base Lightning protocol, per BOLT #1.
git clone git://git.ppad.tech/bolt1.git
Log | Files | Refs | README | LICENSE

commit c4d99cf2411b7b18d7d0a25d0d50a53eac5e7eba
parent 3948fa2302ddc49b547a88fd212db03837558637
Author: Jared Tobin <jared@jtobin.io>
Date:   Sun, 25 Jan 2026 14:39:46 +0400

meta: docs

Diffstat:
Aplans/ARCH3.md | 101+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aplans/IMPL4.md | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 160 insertions(+), 0 deletions(-)

diff --git a/plans/ARCH3.md b/plans/ARCH3.md @@ -0,0 +1,101 @@ +# ARCH3 - Type Safety Improvements + +## Goals + +- Encode fixed-size byte invariants into the type system. +- Enforce TLV stream ordering at construction time. +- Eliminate runtime validation where possible via smart constructors. + +## New Types + +### ChannelId + +32-byte channel identifier used in Error and Warning messages. + +```haskell +newtype ChannelId = ChannelId { unChannelId :: BS.ByteString } + +channelId :: BS.ByteString -> Maybe ChannelId +channelId bs + | BS.length bs == 32 = Just (ChannelId bs) + | otherwise = Nothing + +-- | The all-zeros channel ID (refers to all channels). +allChannels :: ChannelId +``` + +Replaces raw `ByteString` in `Error` and `Warning` message types. + +### ChainHash + +32-byte chain hash used in Init TLV networks field. + +```haskell +newtype ChainHash = ChainHash { unChainHash :: BS.ByteString } + +chainHash :: BS.ByteString -> Maybe ChainHash +chainHash bs + | BS.length bs == 32 = Just (ChainHash bs) + | otherwise = Nothing +``` + +`InitNetworks` changes from `[BS.ByteString]` to `[ChainHash]`. + +### Ordered TlvStream + +TLV streams must have strictly increasing type values. Currently +validated at decode time but not enforced at construction. + +```haskell +newtype TlvStream = TlvStream { unTlvStream :: [TlvRecord] } + +-- | Smart constructor that validates ordering. +tlvStream :: [TlvRecord] -> Maybe TlvStream + +-- | Build from records known to be ordered (internal use). +unsafeTlvStream :: [TlvRecord] -> TlvStream +``` + +The raw constructor becomes internal; external code uses the smart +constructor or decode functions (which validate ordering). + +## Module Changes + +### Lightning.Protocol.BOLT1.Message + +- Add `ChannelId` newtype + smart constructor + `allChannels`. +- Update `Error` and `Warning` to use `ChannelId`. +- Update `InitNetworks` to use `[ChainHash]`. +- Add `ChainHash` newtype + smart constructor. + +### Lightning.Protocol.BOLT1.TLV + +- Hide `TlvStream` constructor from public API. +- Export `tlvStream` smart constructor. +- Export `unsafeTlvStream` for internal/advanced use. +- Decoders already validate ordering; no changes needed there. + +### Lightning.Protocol.BOLT1.Codec + +- Update Error/Warning encode/decode to use `ChannelId`. +- Update Init TLV encode/decode to use `ChainHash`. + +### Lightning.Protocol.BOLT1 + +- Re-export new types and constructors. + +## Validation Strategy + +- `ChannelId` and `ChainHash`: validate length at construction. +- `TlvStream`: validate strictly-increasing types at construction. +- Decoders produce validated types directly. +- Encoders accept only validated types (no runtime checks needed). + +## API Impact + +Breaking changes to: +- `Error` and `Warning` record fields (ByteString -> ChannelId). +- `InitNetworks` constructor (ByteString list -> ChainHash list). +- `TlvStream` constructor (now hidden; use smart constructor). + +These are source-breaking but type-safe migrations. diff --git a/plans/IMPL4.md b/plans/IMPL4.md @@ -0,0 +1,59 @@ +# IMPL4 - Type Safety Improvements + +## Phase 1: ChannelId Newtype + +- Add `ChannelId` newtype to Message module. +- Add `channelId` smart constructor (validates 32 bytes). +- Add `allChannels` constant for the all-zeros channel ID. +- Update `Error` record: `errorChannelId :: !ChannelId`. +- Update `Warning` record: `warningChannelId :: !ChannelId`. +- Update Codec encode/decode functions for Error and Warning. +- Update tests to use new constructors. + +## Phase 2: ChainHash Newtype + +- Add `ChainHash` newtype to Message module. +- Add `chainHash` smart constructor (validates 32 bytes). +- Update `InitNetworks` variant: `InitNetworks ![ChainHash]`. +- Update TLV `parseInitTlvs` to produce `[ChainHash]`. +- Update TLV `encodeInitTlvs` to accept `[ChainHash]`. +- Update tests to use new constructors. + +## Phase 3: Ordered TlvStream + +- Add `tlvStream` smart constructor to TLV module. +- Add `unsafeTlvStream` for internal/trusted use. +- Hide `TlvStream` data constructor from public exports. +- Update re-exports in main module. +- Add tests for ordering validation. + +## Phase 4: Documentation and Cleanup + +- Add Haddock for new types and constructors. +- Update any examples in documentation. +- Verify all tests pass. +- Run benchmarks to ensure no performance regression. + +## Independent Work Chunks + +Phases 1-3 can be done in parallel: +- Phase 1 (ChannelId) touches Message + Codec + Error/Warning tests. +- Phase 2 (ChainHash) touches Message + TLV + Init tests. +- Phase 3 (TlvStream) touches TLV module + TLV tests. + +Phase 4 depends on 1-3 completing. + +## Test Updates + +Each phase requires corresponding test updates: +- Phase 1: Error/Warning encode/decode tests. +- Phase 2: Init TLV parsing tests, network chain tests. +- Phase 3: TlvStream construction tests (valid ordering, rejection). + +## Notes + +- Keep `Eq`, `Show`, `NFData` instances for all new types. +- Consider `IsString` instance for `ChannelId`/`ChainHash` if hex + literals are useful in tests (optional). +- Benchmark decode paths to verify no regression from added + newtype unwrapping.