commit 95a28c02b08a093ced8d55bb67ed0d15ae7275d0
parent ccf14f176a1f49c1bf959d448f74f138bcb2b358
Author: Jared Tobin <jared@jtobin.io>
Date: Sat, 10 Oct 2026 14:36:16 +0400
docs: README, CHANGELOG, CLAUDE.md and AGENTS.md for 0.1.0
Diffstat:
| M | AGENTS.md | | | 6 | +++--- |
| M | CHANGELOG | | | 38 | ++++++++++++++++++++++++++++++++++++++ |
| M | CLAUDE.md | | | 6 | +++--- |
| M | README.md | | | 145 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------ |
4 files changed, 146 insertions(+), 49 deletions(-)
diff --git a/AGENTS.md b/AGENTS.md
@@ -6,10 +6,11 @@ Specification: https://github.com/lightning/bolts/blob/master/07-routing-gossip.
## Project Structure
-- `lib/` - library source (Lightning.Protocol.BOLT7)
+- `lib/` - library source (public API: Lightning.Protocol.BOLT7;
+ submodules are internal)
- `test/` - tests (tasty + tasty-hunit)
- `bench/` - benchmarks (criterion for timing, weigh for allocations)
-- `etc/` - reference materials (BOLT spec)
+- `etc/` - reference materials (BOLT spec, pinned upstream commit)
- `flake.nix` - nix flake for dependency and build management
- `ppad-bolt7.cabal` - cabal package definition
- `CLAUDE.md` / `AGENTS.md` - keep these in sync
@@ -135,5 +136,4 @@ When planning work:
The flake.nix follows ppad conventions:
- Uses ppad-nixpkgs as base
- Follows references to avoid duplication
-- Supports LLVM backend via cabal flag
- Provides devShell with ghc, cabal, cc, llvm
diff --git a/CHANGELOG b/CHANGELOG
@@ -0,0 +1,38 @@
+# Changelog
+
+- 0.1.0 (UNRELEASED)
+ * A substantial rewrite, with breaking changes throughout:
+
+ * The API is exported from Lightning.Protocol.BOLT7 alone, and uses
+ snake_case names (e.g. decode_channel_update, cu_timestamp).
+
+ * Fundamental types come from ppad-bolt1 0.1 (Point, ShortChannelId,
+ MilliSatoshi, TLV streams) and feature vectors from ppad-bolt9.
+ The local NodeId, FeatureBits, routing-parameter newtypes,
+ mainnetChainHash and length constants are gone.
+
+ * Decoders take a whole payload and keep every byte: unassigned
+ flag bits, unknown address descriptors and extension TLV records.
+ Encoding a decoded message reproduces it, so its signatures still
+ verify. channel_update models message_flags (must_be_one,
+ dont_forward) and channel_flags (direction, disable).
+
+ * Query messages have typed short channel id lists, query_flags,
+ query_option, timestamps and checksums.
+
+ * Signature digests and checksums take typed messages. Adds
+ signature verification (via ppad-secp256k1), a message
+ dispatcher, and usable_addresses for the receiver's address
+ rules.
+
+ * Fixes: htlc_maximum_msat is mandatory; node_announcement accepts
+ unknown and deprecated address descriptors; malformed query TLV
+ streams, unknown even TLV types and the deprecated zlib encoding are
+ rejected; u16 length prefixes no longer wrap; validation no longer
+ accepts undecodable data or claims to check feature bits.
+
+ * Tests cover the BOLT #7 extended-queries vectors and real mainnet
+ gossip, with signature checks.
+
+- 0.0.1 (2026-04-18)
+ * Initial release.
diff --git a/CLAUDE.md b/CLAUDE.md
@@ -6,10 +6,11 @@ Specification: https://github.com/lightning/bolts/blob/master/07-routing-gossip.
## Project Structure
-- `lib/` - library source (Lightning.Protocol.BOLT7)
+- `lib/` - library source (public API: Lightning.Protocol.BOLT7;
+ submodules are internal)
- `test/` - tests (tasty + tasty-hunit)
- `bench/` - benchmarks (criterion for timing, weigh for allocations)
-- `etc/` - reference materials (BOLT spec)
+- `etc/` - reference materials (BOLT spec, pinned upstream commit)
- `flake.nix` - nix flake for dependency and build management
- `ppad-bolt7.cabal` - cabal package definition
- `CLAUDE.md` / `AGENTS.md` - keep these in sync
@@ -135,5 +136,4 @@ When planning work:
The flake.nix follows ppad conventions:
- Uses ppad-nixpkgs as base
- Follows references to avoid duplication
-- Supports LLVM backend via cabal flag
- Provides devShell with ghc, cabal, cc, llvm
diff --git a/README.md b/README.md
@@ -4,9 +4,15 @@

[](https://docs.ppad.tech/bolt7)
-A pure Haskell implementation of
-[BOLT #7](https://github.com/lightning/bolts/blob/master/07-routing-gossip.md)
-(Lightning Network routing gossip protocol).
+A pure Haskell implementation of Lightning Network routing gossip,
+[BOLT #7][bolt7]: the `channel_announcement`, `node_announcement`,
+`channel_update` and `announcement_signatures` messages, the gossip
+queries, signature digests and verification, and `channel_update`
+checksums.
+
+Decoding keeps every byte of a message (unassigned flag bits, unknown
+address descriptors, extension TLV records), so a decoded message
+re-encodes exactly and its signatures still verify.
## Usage
@@ -14,69 +20,122 @@ A sample GHCi session:
```
> :set -XOverloadedStrings
+ > import qualified Data.ByteString as BS
+ > import qualified Data.ByteString.Base16 as B16
+ > import qualified Lightning.Protocol.BOLT1 as BOLT1
+ > import Lightning.Protocol.BOLT7
>
- > -- import qualified
- > import qualified Lightning.Protocol.BOLT7 as B7
+ > -- a channel_update payload from mainnet gossip
+ > :{
+ | let Just wire = B16.decode $ mconcat
+ | [ "1c3f0859cb3f4494919c3dcc80d22bbf1e25e378bd95f071530833c2371c33d7"
+ | , "0766012ebef1cd79b299a54d0d29bea2ef6feca1d6cfa188864c95d7eea7e9a4"
+ | , "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000"
+ | , "0cf884000bb000016a0a1c760100006400000000000003e80000000000001388"
+ | , "00000000b1069a80fdd9030800000000ffffffed" ]
+ | :}
+ > let Right upd = decode_channel_update wire
+ > format_short_channel_id (cu_short_channel_id upd)
+ "850052x2992x1"
+ > channel_flags_direction (cu_channel_flags upd)
+ NodeOne
+ > cu_fee_proportional_millionths upd
+ 5000
>
- > -- decode a gossip_timestamp_filter from wire bytes (after msg type)
- > -- wire contains: 32-byte chain_hash, 4-byte first_timestamp,
- > -- 4-byte timestamp_range
- > case B7.decodeGossipTimestampFilter wire of
- > Left err -> print err
- > Right (msg, rest) -> print (B7.gossipFilterFirstTimestamp msg)
- 1609459200
+ > -- extension records (here LND's inbound fees) are kept, so the
+ > -- update re-encodes exactly and its signature verifies
+ > BOLT1.un_tlv_stream (cu_tlvs upd)
+ [TlvRecord {tlv_type = 55555, tlv_value = "\NUL\NUL\NUL\NUL\255\255\255\237"}]
+ > encode_channel_update upd == wire
+ True
+ > :{
+ | let Just node_1 = BOLT1.point =<< B16.decode
+ | "02bb5e47ee26aca1654ce092d9d06eac15d953210c737f2220e2d7e54c4f108677"
+ | :}
+ > verify_channel_update node_1 upd
+ True
+ > verify_channel_update node_1 upd { cu_fee_base_msat = 1 }
+ False
>
- > -- construct and encode a gossip_timestamp_filter
- > let msg = B7.GossipTimestampFilter B7.mainnetChainHash 1609459200 86400
- > let encoded = B7.encodeGossipTimestampFilter msg
+ > -- dispatch on a message type, as split off by BOLT1.decode_envelope
+ > fmap message_type (decode_message 258 wire)
+ Right 258
>
- > -- validate a channel_announcement before processing
- > case B7.validateChannelAnnouncement announcement of
- > Left B7.ValidateNodeIdOrdering -> putStrLn "invalid node ordering"
- > Right () -> putStrLn "valid"
+ > -- gossip queries have typed fields
+ > let Just chain = BOLT1.chain_hash (BS.replicate 32 0)
+ > let opt = Just (query_option [WantTimestamps])
+ > let q = QueryChannelRange chain 800000 1000 opt BOLT1.empty_tlv_stream
+ > let Right payload = encode_query_channel_range q
+ > decode_query_channel_range payload == Right q
+ True
>
- > -- compute the hash for signature verification
- > let sigHash = B7.channelAnnouncementHash encodedAnnouncement
- > -- verify signatures with ppad-secp256k1 (separate library)
+ > -- the deprecated zlib encoding is rejected
+ > decode_reply_channel_range (BS.replicate 41 0 <> "\NUL\STX\SOHx")
+ Left (DecodeUnknownEncoding 1)
```
-## Overview
+## Documentation
-ppad-bolt7 provides types and codecs for BOLT #7 gossip messages:
+Haddocks (API documentation, etc.) are hosted at
+[docs.ppad.tech/bolt7](https://docs.ppad.tech/bolt7).
-* `channel_announcement` (256)
-* `node_announcement` (257)
-* `channel_update` (258)
-* `announcement_signatures` (259)
-* `query_short_channel_ids` (261)
-* `reply_short_channel_ids_end` (262)
-* `query_channel_range` (263)
-* `reply_channel_range` (264)
-* `gossip_timestamp_filter` (265)
+## Specification
-## Documentation
+This library implements BOLT #7 as of lightning/bolts commit
+[1aadb719][spec] (2026-09-21); a copy lives in `etc/`. Fundamental
+types come from [ppad-bolt1][bolt1] and feature vectors from
+[ppad-bolt9][bolt9].
-Haddock documentation is available at
-[docs.ppad.tech/bolt7](https://docs.ppad.tech/bolt7).
+Not implemented:
+
+* The zlib encoding (type 1) of `encoded_short_ids` and its kin, which
+ the specification forbids; messages using it fail to decode.
+* Signing. The digest functions give what a node signs, with e.g.
+ [ppad-secp256k1][secp], to create announcements and updates.
+* Rules that need state or chain access: timestamp ordering,
+ blacklisting, funding output checks, chain hash filtering, rebroadcast
+ and pruning policy. Feature-bit rules are left to ppad-bolt9.
+
+Checksums cover a `channel_update`'s extension records, per the
+specification's wording.
+
+## Performance
+
+The aim is best-in-class performance for pure Haskell code. Run the
+benchmarks with:
+
+```
+$ cabal bench
+```
## Security
-This is a pre-release version of the library and makes no claims about
-security whatsoever.
+This library has received no security audit. Decoding validates every
+field the specification constrains, and rejects unknown even TLV types.
+Signature verification uses ppad-secp256k1 and, like libsecp256k1,
+accepts only low-s signatures.
## Development
-A Nix development shell is provided via flake. Enter it with:
+You'll require [Nix][nixos] with [flake][flake] support enabled. Enter a
+development shell with:
```
$ nix develop
```
-Then use `cabal` as usual:
+Then do e.g.:
```
-$ cabal build
-$ cabal test
-$ cabal bench
+$ cabal repl ppad-bolt7
```
+to get a REPL for the main library.
+
+[bolt7]: https://github.com/lightning/bolts/blob/master/07-routing-gossip.md
+[bolt1]: https://git.ppad.tech/bolt1
+[bolt9]: https://git.ppad.tech/bolt9
+[secp]: https://git.ppad.tech/secp256k1
+[spec]: https://github.com/lightning/bolts/tree/1aadb719b4007c4cea0ba6e36b08c4fb53788dee
+[nixos]: https://nixos.org/
+[flake]: https://nixos.org/manual/nix/unstable/command-ref/new-cli/nix3-flake.html