bolt7

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

commit f206f2b3870a44a6b8592edbcb681fb0902f0cca
parent f2ac5cfa92776112fe03cf08487633e203e6eeeb
Author: Jared Tobin <jared@jtobin.io>
Date:   Sun, 25 Jan 2026 15:36:16 +0400

Merge impl/bench: Phase 7 complete benchmarks

Add comprehensive benchmark coverage for all BOLT #7 operations:

Criterion timing benchmarks (bench/Main.hs):
- All 9 message types: encode/decode
- SCID list encoding/decoding (100 items)
- Hash functions: channelAnnouncementHash, nodeAnnouncementHash,
  channelUpdateHash, channelUpdateChecksum
- Validation functions for all message types

Weigh allocation benchmarks (bench/Weight.hs):
- All message types: construct/encode/decode
- SCID list encoding/decoding
- Hash functions
- Validation functions

Diffstat:
Mbench/Main.hs | 164+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Mbench/Weight.hs | 154++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
2 files changed, 310 insertions(+), 8 deletions(-)

diff --git a/bench/Main.hs b/bench/Main.hs @@ -63,14 +63,14 @@ testChannelId = case channelId zeroBytes32 of Nothing -> error "testChannelId: invalid" {-# NOINLINE testChannelId #-} --- | 33-byte node ID. +-- | 33-byte node ID (03 prefix). testNodeId :: NodeId testNodeId = case nodeId (BS.cons 0x03 zeroBytes32) of Just n -> n Nothing -> error "testNodeId: invalid" {-# NOINLINE testNodeId #-} --- | Second node ID. +-- | Second node ID (02 prefix, lexicographically smaller). testNodeId2 :: NodeId testNodeId2 = case nodeId (BS.cons 0x02 zeroBytes32) of Just n -> n @@ -91,6 +91,13 @@ testAlias = case alias zeroBytes32 of Nothing -> error "testAlias: invalid" {-# NOINLINE testAlias #-} +-- | IPv4 address. +testIPv4 :: IPv4Addr +testIPv4 = case ipv4Addr (BS.pack [127, 0, 0, 1]) of + Just a -> a + Nothing -> error "testIPv4: invalid" +{-# NOINLINE testIPv4 #-} + -- | Empty TLV stream. emptyTlvs :: TlvStream emptyTlvs = unsafeTlvStream [] @@ -101,6 +108,20 @@ emptyFeatures :: FeatureBits emptyFeatures = featureBits BS.empty {-# NOINLINE emptyFeatures #-} +-- | List of test SCIDs for list encoding benchmarks. +testScidList :: [ShortChannelId] +testScidList = map mkScid [1..100] + where + mkScid n = case shortChannelId (BS.pack [0, 0, 0, n, 0, 0, 0, n]) of + Just s -> s + Nothing -> error "mkScid: invalid" +{-# NOINLINE testScidList #-} + +-- | Encoded SCID list for decode benchmarks. +encodedScidList :: BS.ByteString +encodedScidList = encodeShortChannelIdList testScidList +{-# NOINLINE encodedScidList #-} + -- Test messages --------------------------------------------------------------- -- | Test ChannelAnnouncement message. @@ -113,8 +134,8 @@ testChannelAnnouncement = ChannelAnnouncement , channelAnnFeatures = emptyFeatures , channelAnnChainHash = testChainHash , channelAnnShortChanId = testShortChannelId - , channelAnnNodeId1 = testNodeId - , channelAnnNodeId2 = testNodeId2 + , channelAnnNodeId1 = testNodeId2 -- 02... (smaller) + , channelAnnNodeId2 = testNodeId -- 03... (larger) , channelAnnBitcoinKey1 = testPoint , channelAnnBitcoinKey2 = testPoint } @@ -125,6 +146,26 @@ encodedChannelAnnouncement :: BS.ByteString encodedChannelAnnouncement = encodeChannelAnnouncement testChannelAnnouncement {-# NOINLINE encodedChannelAnnouncement #-} +-- | Test NodeAnnouncement message. +testNodeAnnouncement :: NodeAnnouncement +testNodeAnnouncement = NodeAnnouncement + { nodeAnnSignature = testSignature + , nodeAnnFeatures = emptyFeatures + , nodeAnnTimestamp = 1234567890 + , nodeAnnNodeId = testNodeId + , nodeAnnRgbColor = testRgbColor + , nodeAnnAlias = testAlias + , nodeAnnAddresses = [AddrIPv4 testIPv4 9735] + } +{-# NOINLINE testNodeAnnouncement #-} + +-- | Encoded NodeAnnouncement for decode benchmarks. +encodedNodeAnnouncement :: BS.ByteString +encodedNodeAnnouncement = case encodeNodeAnnouncement testNodeAnnouncement of + Right bs -> bs + Left _ -> error "encodedNodeAnnouncement: encode failed" +{-# NOINLINE encodedNodeAnnouncement #-} + -- | Test ChannelUpdate message. testChannelUpdate :: ChannelUpdate testChannelUpdate = ChannelUpdate @@ -163,6 +204,71 @@ encodedAnnouncementSignatures = encodeAnnouncementSignatures testAnnouncementSignatures {-# NOINLINE encodedAnnouncementSignatures #-} +-- | Test QueryShortChannelIds message. +testQueryShortChannelIds :: QueryShortChannelIds +testQueryShortChannelIds = QueryShortChannelIds + { queryScidsChainHash = testChainHash + , queryScidsData = encodeShortChannelIdList [testShortChannelId] + , queryScidsTlvs = emptyTlvs + } +{-# NOINLINE testQueryShortChannelIds #-} + +-- | Encoded QueryShortChannelIds for decode benchmarks. +encodedQueryShortChannelIds :: BS.ByteString +encodedQueryShortChannelIds = + case encodeQueryShortChannelIds testQueryShortChannelIds of + Right bs -> bs + Left _ -> error "encodedQueryShortChannelIds: encode failed" +{-# NOINLINE encodedQueryShortChannelIds #-} + +-- | Test ReplyShortChannelIdsEnd message. +testReplyShortChannelIdsEnd :: ReplyShortChannelIdsEnd +testReplyShortChannelIdsEnd = ReplyShortChannelIdsEnd + { replyScidsChainHash = testChainHash + , replyScidsFullInfo = 1 + } +{-# NOINLINE testReplyShortChannelIdsEnd #-} + +-- | Encoded ReplyShortChannelIdsEnd for decode benchmarks. +encodedReplyShortChannelIdsEnd :: BS.ByteString +encodedReplyShortChannelIdsEnd = + encodeReplyShortChannelIdsEnd testReplyShortChannelIdsEnd +{-# NOINLINE encodedReplyShortChannelIdsEnd #-} + +-- | Test QueryChannelRange message. +testQueryChannelRange :: QueryChannelRange +testQueryChannelRange = QueryChannelRange + { queryRangeChainHash = testChainHash + , queryRangeFirstBlock = 700000 + , queryRangeNumBlocks = 10000 + , queryRangeTlvs = emptyTlvs + } +{-# NOINLINE testQueryChannelRange #-} + +-- | Encoded QueryChannelRange for decode benchmarks. +encodedQueryChannelRange :: BS.ByteString +encodedQueryChannelRange = encodeQueryChannelRange testQueryChannelRange +{-# NOINLINE encodedQueryChannelRange #-} + +-- | Test ReplyChannelRange message. +testReplyChannelRange :: ReplyChannelRange +testReplyChannelRange = ReplyChannelRange + { replyRangeChainHash = testChainHash + , replyRangeFirstBlock = 700000 + , replyRangeNumBlocks = 10000 + , replyRangeSyncComplete = 1 + , replyRangeData = encodeShortChannelIdList [testShortChannelId] + , replyRangeTlvs = emptyTlvs + } +{-# NOINLINE testReplyChannelRange #-} + +-- | Encoded ReplyChannelRange for decode benchmarks. +encodedReplyChannelRange :: BS.ByteString +encodedReplyChannelRange = case encodeReplyChannelRange testReplyChannelRange of + Right bs -> bs + Left _ -> error "encodedReplyChannelRange: encode failed" +{-# NOINLINE encodedReplyChannelRange #-} + -- | Test GossipTimestampFilter message. testGossipTimestampFilter :: GossipTimestampFilter testGossipTimestampFilter = GossipTimestampFilter @@ -186,6 +292,10 @@ main = defaultMain [ bench "encode" $ nf encodeChannelAnnouncement testChannelAnnouncement , bench "decode" $ nf decodeChannelAnnouncement encodedChannelAnnouncement ] + , bgroup "node_announcement" + [ bench "encode" $ nf encodeNodeAnnouncement testNodeAnnouncement + , bench "decode" $ nf decodeNodeAnnouncement encodedNodeAnnouncement + ] , bgroup "channel_update" [ bench "encode" $ nf encodeChannelUpdate testChannelUpdate , bench "decode" $ nf decodeChannelUpdate encodedChannelUpdate @@ -196,10 +306,56 @@ main = defaultMain , bench "decode" $ nf decodeAnnouncementSignatures encodedAnnouncementSignatures ] + , bgroup "query_short_channel_ids" + [ bench "encode" $ + nf encodeQueryShortChannelIds testQueryShortChannelIds + , bench "decode" $ + nf decodeQueryShortChannelIds encodedQueryShortChannelIds + ] + , bgroup "reply_short_channel_ids_end" + [ bench "encode" $ + nf encodeReplyShortChannelIdsEnd testReplyShortChannelIdsEnd + , bench "decode" $ + nf decodeReplyShortChannelIdsEnd encodedReplyShortChannelIdsEnd + ] + , bgroup "query_channel_range" + [ bench "encode" $ nf encodeQueryChannelRange testQueryChannelRange + , bench "decode" $ nf decodeQueryChannelRange encodedQueryChannelRange + ] + , bgroup "reply_channel_range" + [ bench "encode" $ nf encodeReplyChannelRange testReplyChannelRange + , bench "decode" $ nf decodeReplyChannelRange encodedReplyChannelRange + ] , bgroup "gossip_timestamp_filter" [ bench "encode" $ nf encodeGossipTimestampFilter testGossipTimestampFilter , bench "decode" $ nf decodeGossipTimestampFilter encodedGossipTimestampFilter ] + , bgroup "scid_list" + [ bench "encode (100)" $ nf encodeShortChannelIdList testScidList + , bench "decode (100)" $ nf decodeShortChannelIdList encodedScidList + ] + , bgroup "hash" + [ bench "channelAnnouncementHash" $ + nf channelAnnouncementHash encodedChannelAnnouncement + , bench "nodeAnnouncementHash" $ + nf nodeAnnouncementHash encodedNodeAnnouncement + , bench "channelUpdateHash" $ + nf channelUpdateHash encodedChannelUpdate + , bench "channelUpdateChecksum" $ + nf channelUpdateChecksum encodedChannelUpdate + ] + , bgroup "validate" + [ bench "channelAnnouncement" $ + nf validateChannelAnnouncement testChannelAnnouncement + , bench "nodeAnnouncement" $ + nf validateNodeAnnouncement testNodeAnnouncement + , bench "channelUpdate" $ + nf validateChannelUpdate testChannelUpdate + , bench "queryChannelRange" $ + nf validateQueryChannelRange testQueryChannelRange + , bench "replyChannelRange" $ + nf validateReplyChannelRange testReplyChannelRange + ] ] diff --git a/bench/Weight.hs b/bench/Weight.hs @@ -12,6 +12,7 @@ module Main where import qualified Data.ByteString as BS +import Lightning.Protocol.BOLT1 (TlvStream, unsafeTlvStream) import Lightning.Protocol.BOLT7 import Weigh @@ -62,25 +63,65 @@ testChannelId = case channelId zeroBytes32 of Nothing -> error "testChannelId: invalid" {-# NOINLINE testChannelId #-} --- | 33-byte node ID. +-- | 33-byte node ID (03 prefix). testNodeId :: NodeId testNodeId = case nodeId (BS.cons 0x03 zeroBytes32) of Just n -> n Nothing -> error "testNodeId: invalid" {-# NOINLINE testNodeId #-} --- | Second node ID. +-- | Second node ID (02 prefix, lexicographically smaller). testNodeId2 :: NodeId testNodeId2 = case nodeId (BS.cons 0x02 zeroBytes32) of Just n -> n Nothing -> error "testNodeId2: invalid" {-# NOINLINE testNodeId2 #-} +-- | RGB color. +testRgbColor :: RgbColor +testRgbColor = case rgbColor (BS.pack [0xff, 0x00, 0x00]) of + Just c -> c + Nothing -> error "testRgbColor: invalid" +{-# NOINLINE testRgbColor #-} + +-- | 32-byte alias. +testAlias :: Alias +testAlias = case alias zeroBytes32 of + Just a -> a + Nothing -> error "testAlias: invalid" +{-# NOINLINE testAlias #-} + +-- | IPv4 address. +testIPv4 :: IPv4Addr +testIPv4 = case ipv4Addr (BS.pack [127, 0, 0, 1]) of + Just a -> a + Nothing -> error "testIPv4: invalid" +{-# NOINLINE testIPv4 #-} + -- | Empty feature bits. emptyFeatures :: FeatureBits emptyFeatures = featureBits BS.empty {-# NOINLINE emptyFeatures #-} +-- | Empty TLV stream. +emptyTlvs :: TlvStream +emptyTlvs = unsafeTlvStream [] +{-# NOINLINE emptyTlvs #-} + +-- | List of test SCIDs for list encoding benchmarks. +testScidList :: [ShortChannelId] +testScidList = map mkScid [1..100] + where + mkScid n = case shortChannelId (BS.pack [0, 0, 0, n, 0, 0, 0, n]) of + Just s -> s + Nothing -> error "mkScid: invalid" +{-# NOINLINE testScidList #-} + +-- | Encoded SCID list for decode benchmarks. +encodedScidList :: BS.ByteString +encodedScidList = encodeShortChannelIdList testScidList +{-# NOINLINE encodedScidList #-} + -- Message constructors -------------------------------------------------------- -- | Construct ChannelAnnouncement message. @@ -103,6 +144,20 @@ mkChannelAnnouncement !ns1 !ns2 !ch !scid !nid1 !nid2 !bk1 !bk2 !feat = , channelAnnBitcoinKey2 = bk2 } +-- | Construct NodeAnnouncement message. +mkNodeAnnouncement + :: Signature -> FeatureBits -> NodeId -> RgbColor -> Alias + -> [Address] -> NodeAnnouncement +mkNodeAnnouncement !sig !feat !nid !col !al !addrs = NodeAnnouncement + { nodeAnnSignature = sig + , nodeAnnFeatures = feat + , nodeAnnTimestamp = 1234567890 + , nodeAnnNodeId = nid + , nodeAnnRgbColor = col + , nodeAnnAlias = al + , nodeAnnAddresses = addrs + } + -- | Construct ChannelUpdate message. mkChannelUpdate :: Signature -> ChainHash -> ShortChannelId -> ChannelUpdate mkChannelUpdate !sig !ch !scid = ChannelUpdate @@ -137,13 +192,33 @@ mkGossipTimestampFilter !ch = GossipTimestampFilter , gossipFilterTimestampRange = 86400 } +-- | Construct QueryChannelRange message. +mkQueryChannelRange :: ChainHash -> TlvStream -> QueryChannelRange +mkQueryChannelRange !ch !tlvs = QueryChannelRange + { queryRangeChainHash = ch + , queryRangeFirstBlock = 700000 + , queryRangeNumBlocks = 10000 + , queryRangeTlvs = tlvs + } + +-- | Construct ReplyChannelRange message. +mkReplyChannelRange :: ChainHash -> TlvStream -> ReplyChannelRange +mkReplyChannelRange !ch !tlvs = ReplyChannelRange + { replyRangeChainHash = ch + , replyRangeFirstBlock = 700000 + , replyRangeNumBlocks = 10000 + , replyRangeSyncComplete = 1 + , replyRangeData = encodeShortChannelIdList [testShortChannelId] + , replyRangeTlvs = tlvs + } + -- Pre-constructed messages ---------------------------------------------------- -- | Test ChannelAnnouncement message. testChannelAnnouncement :: ChannelAnnouncement testChannelAnnouncement = mkChannelAnnouncement testSignature testSignature testChainHash testShortChannelId - testNodeId testNodeId2 testPoint testPoint emptyFeatures + testNodeId2 testNodeId testPoint testPoint emptyFeatures {-# NOINLINE testChannelAnnouncement #-} -- | Encoded ChannelAnnouncement for decode benchmarks. @@ -151,6 +226,20 @@ encodedChannelAnnouncement :: BS.ByteString encodedChannelAnnouncement = encodeChannelAnnouncement testChannelAnnouncement {-# NOINLINE encodedChannelAnnouncement #-} +-- | Test NodeAnnouncement message. +testNodeAnnouncement :: NodeAnnouncement +testNodeAnnouncement = mkNodeAnnouncement + testSignature emptyFeatures testNodeId testRgbColor testAlias + [AddrIPv4 testIPv4 9735] +{-# NOINLINE testNodeAnnouncement #-} + +-- | Encoded NodeAnnouncement for decode benchmarks. +encodedNodeAnnouncement :: BS.ByteString +encodedNodeAnnouncement = case encodeNodeAnnouncement testNodeAnnouncement of + Right bs -> bs + Left _ -> error "encodedNodeAnnouncement: encode failed" +{-# NOINLINE encodedNodeAnnouncement #-} + -- | Test ChannelUpdate message. testChannelUpdate :: ChannelUpdate testChannelUpdate = mkChannelUpdate testSignature testChainHash testShortChannelId @@ -184,6 +273,28 @@ encodedGossipTimestampFilter = encodeGossipTimestampFilter testGossipTimestampFilter {-# NOINLINE encodedGossipTimestampFilter #-} +-- | Test QueryChannelRange message. +testQueryChannelRange :: QueryChannelRange +testQueryChannelRange = mkQueryChannelRange testChainHash emptyTlvs +{-# NOINLINE testQueryChannelRange #-} + +-- | Encoded QueryChannelRange for decode benchmarks. +encodedQueryChannelRange :: BS.ByteString +encodedQueryChannelRange = encodeQueryChannelRange testQueryChannelRange +{-# NOINLINE encodedQueryChannelRange #-} + +-- | Test ReplyChannelRange message. +testReplyChannelRange :: ReplyChannelRange +testReplyChannelRange = mkReplyChannelRange testChainHash emptyTlvs +{-# NOINLINE testReplyChannelRange #-} + +-- | Encoded ReplyChannelRange for decode benchmarks. +encodedReplyChannelRange :: BS.ByteString +encodedReplyChannelRange = case encodeReplyChannelRange testReplyChannelRange of + Right bs -> bs + Left _ -> error "encodedReplyChannelRange: encode failed" +{-# NOINLINE encodedReplyChannelRange #-} + -- Weigh benchmarks ------------------------------------------------------------ main :: IO () @@ -191,10 +302,17 @@ main = mainWith $ do wgroup "channel_announcement" $ do func "construct" (mkChannelAnnouncement testSignature testSignature testChainHash testShortChannelId - testNodeId testNodeId2 testPoint testPoint) emptyFeatures + testNodeId2 testNodeId testPoint testPoint) emptyFeatures func "encode" encodeChannelAnnouncement testChannelAnnouncement func "decode" decodeChannelAnnouncement encodedChannelAnnouncement + wgroup "node_announcement" $ do + func "construct" (mkNodeAnnouncement + testSignature emptyFeatures testNodeId testRgbColor testAlias) + [AddrIPv4 testIPv4 9735] + func "encode" encodeNodeAnnouncement testNodeAnnouncement + func "decode" decodeNodeAnnouncement encodedNodeAnnouncement + wgroup "channel_update" $ do func "construct" (mkChannelUpdate testSignature testChainHash) testShortChannelId @@ -207,7 +325,35 @@ main = mainWith $ do func "encode" encodeAnnouncementSignatures testAnnouncementSignatures func "decode" decodeAnnouncementSignatures encodedAnnouncementSignatures + wgroup "query_channel_range" $ do + func "construct" (mkQueryChannelRange testChainHash) emptyTlvs + func "encode" encodeQueryChannelRange testQueryChannelRange + func "decode" decodeQueryChannelRange encodedQueryChannelRange + + wgroup "reply_channel_range" $ do + func "construct" (mkReplyChannelRange testChainHash) emptyTlvs + func "encode" encodeReplyChannelRange testReplyChannelRange + func "decode" decodeReplyChannelRange encodedReplyChannelRange + wgroup "gossip_timestamp_filter" $ do func "construct" mkGossipTimestampFilter testChainHash func "encode" encodeGossipTimestampFilter testGossipTimestampFilter func "decode" decodeGossipTimestampFilter encodedGossipTimestampFilter + + wgroup "scid_list" $ do + func "encode (100)" encodeShortChannelIdList testScidList + func "decode (100)" decodeShortChannelIdList encodedScidList + + wgroup "hash" $ do + func "channelAnnouncementHash" channelAnnouncementHash + encodedChannelAnnouncement + func "nodeAnnouncementHash" nodeAnnouncementHash encodedNodeAnnouncement + func "channelUpdateHash" channelUpdateHash encodedChannelUpdate + func "channelUpdateChecksum" channelUpdateChecksum encodedChannelUpdate + + wgroup "validate" $ do + func "channelAnnouncement" validateChannelAnnouncement testChannelAnnouncement + func "nodeAnnouncement" validateNodeAnnouncement testNodeAnnouncement + func "channelUpdate" validateChannelUpdate testChannelUpdate + func "queryChannelRange" validateQueryChannelRange testQueryChannelRange + func "replyChannelRange" validateReplyChannelRange testReplyChannelRange