bolt7

Routing gossip protocol, per BOLT #7 (docs.ppad.tech/bolt7).
git clone git://git.ppad.tech/bolt7.git
Log | Files | Refs | README | LICENSE

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:
MAGENTS.md | 6+++---
MCHANGELOG | 38++++++++++++++++++++++++++++++++++++++
MCLAUDE.md | 6+++---
MREADME.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://img.shields.io/badge/license-MIT-brightgreen) [![](https://img.shields.io/badge/haddock-bolt7-lightblue)](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