bolt7

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

Main.hs (12704B)


      1 {-# LANGUAGE BangPatterns #-}
      2 {-# LANGUAGE OverloadedStrings #-}
      3 
      4 -- |
      5 -- Module: Main
      6 -- Copyright: (c) 2025 Jared Tobin
      7 -- License: MIT
      8 -- Maintainer: Jared Tobin <jared@ppad.tech>
      9 --
     10 -- Criterion timing benchmarks for BOLT #7 gossip codecs.
     11 
     12 module Main where
     13 
     14 import Criterion.Main
     15 import qualified Data.ByteString as BS
     16 import Lightning.Protocol.BOLT1 (TlvStream, unsafeTlvStream)
     17 import Lightning.Protocol.BOLT7
     18 
     19 -- Test data construction ------------------------------------------------------
     20 
     21 -- | 32 zero bytes for chain hashes, etc.
     22 zeroBytes32 :: BS.ByteString
     23 zeroBytes32 = BS.replicate 32 0x00
     24 {-# NOINLINE zeroBytes32 #-}
     25 
     26 -- | 8 zero bytes for short channel IDs.
     27 zeroBytes8 :: BS.ByteString
     28 zeroBytes8 = BS.replicate 8 0x00
     29 {-# NOINLINE zeroBytes8 #-}
     30 
     31 -- | 64-byte signature.
     32 testSignature :: Signature
     33 testSignature = case signature (BS.replicate 64 0x01) of
     34   Just s  -> s
     35   Nothing -> error "testSignature: invalid"
     36 {-# NOINLINE testSignature #-}
     37 
     38 -- | 33-byte compressed public key (02 prefix + 32 zero bytes).
     39 testPoint :: Point
     40 testPoint = case point (BS.cons 0x02 zeroBytes32) of
     41   Just p  -> p
     42   Nothing -> error "testPoint: invalid"
     43 {-# NOINLINE testPoint #-}
     44 
     45 -- | 32-byte chain hash.
     46 testChainHash :: ChainHash
     47 testChainHash = case chainHash zeroBytes32 of
     48   Just h  -> h
     49   Nothing -> error "testChainHash: invalid"
     50 {-# NOINLINE testChainHash #-}
     51 
     52 -- | 8-byte short channel ID.
     53 testShortChannelId :: ShortChannelId
     54 testShortChannelId = case scidFromBytes zeroBytes8 of
     55   Just s  -> s
     56   Nothing -> error "testShortChannelId: invalid"
     57 {-# NOINLINE testShortChannelId #-}
     58 
     59 -- | 32-byte channel ID.
     60 testChannelId :: ChannelId
     61 testChannelId = case channelId zeroBytes32 of
     62   Just c  -> c
     63   Nothing -> error "testChannelId: invalid"
     64 {-# NOINLINE testChannelId #-}
     65 
     66 -- | 33-byte node ID (03 prefix).
     67 testNodeId :: NodeId
     68 testNodeId = case nodeId (BS.cons 0x03 zeroBytes32) of
     69   Just n  -> n
     70   Nothing -> error "testNodeId: invalid"
     71 {-# NOINLINE testNodeId #-}
     72 
     73 -- | Second node ID (02 prefix, lexicographically smaller).
     74 testNodeId2 :: NodeId
     75 testNodeId2 = case nodeId (BS.cons 0x02 zeroBytes32) of
     76   Just n  -> n
     77   Nothing -> error "testNodeId2: invalid"
     78 {-# NOINLINE testNodeId2 #-}
     79 
     80 -- | RGB color.
     81 testRgbColor :: RgbColor
     82 testRgbColor = case rgbColor (BS.pack [0xff, 0x00, 0x00]) of
     83   Just c  -> c
     84   Nothing -> error "testRgbColor: invalid"
     85 {-# NOINLINE testRgbColor #-}
     86 
     87 -- | 32-byte alias.
     88 testAlias :: Alias
     89 testAlias = case alias zeroBytes32 of
     90   Just a  -> a
     91   Nothing -> error "testAlias: invalid"
     92 {-# NOINLINE testAlias #-}
     93 
     94 -- | IPv4 address.
     95 testIPv4 :: IPv4Addr
     96 testIPv4 = case ipv4Addr (BS.pack [127, 0, 0, 1]) of
     97   Just a  -> a
     98   Nothing -> error "testIPv4: invalid"
     99 {-# NOINLINE testIPv4 #-}
    100 
    101 -- | Empty TLV stream.
    102 emptyTlvs :: TlvStream
    103 emptyTlvs = unsafeTlvStream []
    104 {-# NOINLINE emptyTlvs #-}
    105 
    106 -- | Empty feature bits.
    107 emptyFeatures :: FeatureBits
    108 emptyFeatures = featureBits BS.empty
    109 {-# NOINLINE emptyFeatures #-}
    110 
    111 -- | List of test SCIDs for list encoding benchmarks.
    112 testScidList :: [ShortChannelId]
    113 testScidList = map mkScid [1..100]
    114   where
    115     mkScid n = case scidFromBytes (BS.pack [0, 0, 0, n, 0, 0, 0, n]) of
    116       Just s  -> s
    117       Nothing -> error "mkScid: invalid"
    118 {-# NOINLINE testScidList #-}
    119 
    120 -- | Encoded SCID list for decode benchmarks.
    121 encodedScidList :: BS.ByteString
    122 encodedScidList = encodeShortChannelIdList testScidList
    123 {-# NOINLINE encodedScidList #-}
    124 
    125 -- Test messages ---------------------------------------------------------------
    126 
    127 -- | Test ChannelAnnouncement message.
    128 testChannelAnnouncement :: ChannelAnnouncement
    129 testChannelAnnouncement = ChannelAnnouncement
    130   { channelAnnNodeSig1     = testSignature
    131   , channelAnnNodeSig2     = testSignature
    132   , channelAnnBitcoinSig1  = testSignature
    133   , channelAnnBitcoinSig2  = testSignature
    134   , channelAnnFeatures     = emptyFeatures
    135   , channelAnnChainHash    = testChainHash
    136   , channelAnnShortChanId  = testShortChannelId
    137   , channelAnnNodeId1      = testNodeId2  -- 02... (smaller)
    138   , channelAnnNodeId2      = testNodeId   -- 03... (larger)
    139   , channelAnnBitcoinKey1  = testPoint
    140   , channelAnnBitcoinKey2  = testPoint
    141   }
    142 {-# NOINLINE testChannelAnnouncement #-}
    143 
    144 -- | Encoded ChannelAnnouncement for decode benchmarks.
    145 encodedChannelAnnouncement :: BS.ByteString
    146 encodedChannelAnnouncement = encodeChannelAnnouncement testChannelAnnouncement
    147 {-# NOINLINE encodedChannelAnnouncement #-}
    148 
    149 -- | Test NodeAnnouncement message.
    150 testNodeAnnouncement :: NodeAnnouncement
    151 testNodeAnnouncement = NodeAnnouncement
    152   { nodeAnnSignature = testSignature
    153   , nodeAnnFeatures  = emptyFeatures
    154   , nodeAnnTimestamp = 1234567890
    155   , nodeAnnNodeId    = testNodeId
    156   , nodeAnnRgbColor  = testRgbColor
    157   , nodeAnnAlias     = testAlias
    158   , nodeAnnAddresses = [AddrIPv4 testIPv4 9735]
    159   }
    160 {-# NOINLINE testNodeAnnouncement #-}
    161 
    162 -- | Encoded NodeAnnouncement for decode benchmarks.
    163 encodedNodeAnnouncement :: BS.ByteString
    164 encodedNodeAnnouncement = case encodeNodeAnnouncement testNodeAnnouncement of
    165   Right bs -> bs
    166   Left _   -> error "encodedNodeAnnouncement: encode failed"
    167 {-# NOINLINE encodedNodeAnnouncement #-}
    168 
    169 -- | Test ChannelUpdate message.
    170 testChannelUpdate :: ChannelUpdate
    171 testChannelUpdate = ChannelUpdate
    172   { chanUpdateSignature       = testSignature
    173   , chanUpdateChainHash       = testChainHash
    174   , chanUpdateShortChanId     = testShortChannelId
    175   , chanUpdateTimestamp       = 1234567890
    176   , chanUpdateChanFlags       = ChannelFlags NodeOne Enabled
    177   , chanUpdateCltvExpDelta    = CltvExpiryDelta 144
    178   , chanUpdateHtlcMinMsat     = HtlcMinimumMsat 1000
    179   , chanUpdateFeeBaseMsat     = FeeBaseMsat 1000
    180   , chanUpdateFeeProportional = FeeProportionalMillionths 100
    181   , chanUpdateHtlcMaxMsat     = Just (HtlcMaximumMsat 1000000000)
    182   }
    183 {-# NOINLINE testChannelUpdate #-}
    184 
    185 -- | Encoded ChannelUpdate for decode benchmarks.
    186 encodedChannelUpdate :: BS.ByteString
    187 encodedChannelUpdate = encodeChannelUpdate testChannelUpdate
    188 {-# NOINLINE encodedChannelUpdate #-}
    189 
    190 -- | Test AnnouncementSignatures message.
    191 testAnnouncementSignatures :: AnnouncementSignatures
    192 testAnnouncementSignatures = AnnouncementSignatures
    193   { annSigChannelId   = testChannelId
    194   , annSigShortChanId = testShortChannelId
    195   , annSigNodeSig     = testSignature
    196   , annSigBitcoinSig  = testSignature
    197   }
    198 {-# NOINLINE testAnnouncementSignatures #-}
    199 
    200 -- | Encoded AnnouncementSignatures for decode benchmarks.
    201 encodedAnnouncementSignatures :: BS.ByteString
    202 encodedAnnouncementSignatures =
    203   encodeAnnouncementSignatures testAnnouncementSignatures
    204 {-# NOINLINE encodedAnnouncementSignatures #-}
    205 
    206 -- | Test QueryShortChannelIds message.
    207 testQueryShortChannelIds :: QueryShortChannelIds
    208 testQueryShortChannelIds = QueryShortChannelIds
    209   { queryScidsChainHash = testChainHash
    210   , queryScidsData      = encodeShortChannelIdList [testShortChannelId]
    211   , queryScidsTlvs      = emptyTlvs
    212   }
    213 {-# NOINLINE testQueryShortChannelIds #-}
    214 
    215 -- | Encoded QueryShortChannelIds for decode benchmarks.
    216 encodedQueryShortChannelIds :: BS.ByteString
    217 encodedQueryShortChannelIds =
    218   case encodeQueryShortChannelIds testQueryShortChannelIds of
    219     Right bs -> bs
    220     Left _   -> error "encodedQueryShortChannelIds: encode failed"
    221 {-# NOINLINE encodedQueryShortChannelIds #-}
    222 
    223 -- | Test ReplyShortChannelIdsEnd message.
    224 testReplyShortChannelIdsEnd :: ReplyShortChannelIdsEnd
    225 testReplyShortChannelIdsEnd = ReplyShortChannelIdsEnd
    226   { replyScidsChainHash = testChainHash
    227   , replyScidsFullInfo  = 1
    228   }
    229 {-# NOINLINE testReplyShortChannelIdsEnd #-}
    230 
    231 -- | Encoded ReplyShortChannelIdsEnd for decode benchmarks.
    232 encodedReplyShortChannelIdsEnd :: BS.ByteString
    233 encodedReplyShortChannelIdsEnd =
    234   encodeReplyShortChannelIdsEnd testReplyShortChannelIdsEnd
    235 {-# NOINLINE encodedReplyShortChannelIdsEnd #-}
    236 
    237 -- | Test QueryChannelRange message.
    238 testQueryChannelRange :: QueryChannelRange
    239 testQueryChannelRange = QueryChannelRange
    240   { queryRangeChainHash  = testChainHash
    241   , queryRangeFirstBlock = BlockHeight 700000
    242   , queryRangeNumBlocks  = BlockCount 10000
    243   , queryRangeTlvs       = emptyTlvs
    244   }
    245 {-# NOINLINE testQueryChannelRange #-}
    246 
    247 -- | Encoded QueryChannelRange for decode benchmarks.
    248 encodedQueryChannelRange :: BS.ByteString
    249 encodedQueryChannelRange = encodeQueryChannelRange testQueryChannelRange
    250 {-# NOINLINE encodedQueryChannelRange #-}
    251 
    252 -- | Test ReplyChannelRange message.
    253 testReplyChannelRange :: ReplyChannelRange
    254 testReplyChannelRange = ReplyChannelRange
    255   { replyRangeChainHash    = testChainHash
    256   , replyRangeFirstBlock   = BlockHeight 700000
    257   , replyRangeNumBlocks    = BlockCount 10000
    258   , replyRangeSyncComplete = 1
    259   , replyRangeData         = encodeShortChannelIdList [testShortChannelId]
    260   , replyRangeTlvs         = emptyTlvs
    261   }
    262 {-# NOINLINE testReplyChannelRange #-}
    263 
    264 -- | Encoded ReplyChannelRange for decode benchmarks.
    265 encodedReplyChannelRange :: BS.ByteString
    266 encodedReplyChannelRange = case encodeReplyChannelRange testReplyChannelRange of
    267   Right bs -> bs
    268   Left _   -> error "encodedReplyChannelRange: encode failed"
    269 {-# NOINLINE encodedReplyChannelRange #-}
    270 
    271 -- | Test GossipTimestampFilter message.
    272 testGossipTimestampFilter :: GossipTimestampFilter
    273 testGossipTimestampFilter = GossipTimestampFilter
    274   { gossipFilterChainHash      = testChainHash
    275   , gossipFilterFirstTimestamp = 1609459200
    276   , gossipFilterTimestampRange = 86400
    277   }
    278 {-# NOINLINE testGossipTimestampFilter #-}
    279 
    280 -- | Encoded GossipTimestampFilter for decode benchmarks.
    281 encodedGossipTimestampFilter :: BS.ByteString
    282 encodedGossipTimestampFilter =
    283   encodeGossipTimestampFilter testGossipTimestampFilter
    284 {-# NOINLINE encodedGossipTimestampFilter #-}
    285 
    286 -- Benchmark groups ------------------------------------------------------------
    287 
    288 main :: IO ()
    289 main = defaultMain
    290   [ bgroup "channel_announcement"
    291       [ bench "encode" $ nf encodeChannelAnnouncement testChannelAnnouncement
    292       , bench "decode" $ nf decodeChannelAnnouncement encodedChannelAnnouncement
    293       ]
    294   , bgroup "node_announcement"
    295       [ bench "encode" $ nf encodeNodeAnnouncement testNodeAnnouncement
    296       , bench "decode" $ nf decodeNodeAnnouncement encodedNodeAnnouncement
    297       ]
    298   , bgroup "channel_update"
    299       [ bench "encode" $ nf encodeChannelUpdate testChannelUpdate
    300       , bench "decode" $ nf decodeChannelUpdate encodedChannelUpdate
    301       ]
    302   , bgroup "announcement_signatures"
    303       [ bench "encode" $
    304           nf encodeAnnouncementSignatures testAnnouncementSignatures
    305       , bench "decode" $
    306           nf decodeAnnouncementSignatures encodedAnnouncementSignatures
    307       ]
    308   , bgroup "query_short_channel_ids"
    309       [ bench "encode" $
    310           nf encodeQueryShortChannelIds testQueryShortChannelIds
    311       , bench "decode" $
    312           nf decodeQueryShortChannelIds encodedQueryShortChannelIds
    313       ]
    314   , bgroup "reply_short_channel_ids_end"
    315       [ bench "encode" $
    316           nf encodeReplyShortChannelIdsEnd testReplyShortChannelIdsEnd
    317       , bench "decode" $
    318           nf decodeReplyShortChannelIdsEnd encodedReplyShortChannelIdsEnd
    319       ]
    320   , bgroup "query_channel_range"
    321       [ bench "encode" $ nf encodeQueryChannelRange testQueryChannelRange
    322       , bench "decode" $ nf decodeQueryChannelRange encodedQueryChannelRange
    323       ]
    324   , bgroup "reply_channel_range"
    325       [ bench "encode" $ nf encodeReplyChannelRange testReplyChannelRange
    326       , bench "decode" $ nf decodeReplyChannelRange encodedReplyChannelRange
    327       ]
    328   , bgroup "gossip_timestamp_filter"
    329       [ bench "encode" $
    330           nf encodeGossipTimestampFilter testGossipTimestampFilter
    331       , bench "decode" $
    332           nf decodeGossipTimestampFilter encodedGossipTimestampFilter
    333       ]
    334   , bgroup "scid_list"
    335       [ bench "encode (100)" $ nf encodeShortChannelIdList testScidList
    336       , bench "decode (100)" $ nf decodeShortChannelIdList encodedScidList
    337       ]
    338   , bgroup "hash"
    339       [ bench "channelAnnouncementHash" $
    340           nf channelAnnouncementHash encodedChannelAnnouncement
    341       , bench "nodeAnnouncementHash" $
    342           nf nodeAnnouncementHash encodedNodeAnnouncement
    343       , bench "channelUpdateHash" $
    344           nf channelUpdateHash encodedChannelUpdate
    345       , bench "channelUpdateChecksum" $
    346           nf channelUpdateChecksum encodedChannelUpdate
    347       ]
    348   , bgroup "validate"
    349       [ bench "channelAnnouncement" $
    350           nf validateChannelAnnouncement testChannelAnnouncement
    351       , bench "nodeAnnouncement" $
    352           nf validateNodeAnnouncement testNodeAnnouncement
    353       , bench "channelUpdate" $
    354           nf validateChannelUpdate testChannelUpdate
    355       , bench "queryChannelRange" $
    356           nf validateQueryChannelRange testQueryChannelRange
    357       , bench "replyChannelRange" $
    358           nf validateReplyChannelRange testReplyChannelRange
    359       ]
    360   ]