commit c4d99cf2411b7b18d7d0a25d0d50a53eac5e7eba
parent 3948fa2302ddc49b547a88fd212db03837558637
Author: Jared Tobin <jared@jtobin.io>
Date: Sun, 25 Jan 2026 14:39:46 +0400
meta: docs
Diffstat:
| A | plans/ARCH3.md | | | 101 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | plans/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.