bolt7

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

Weight.hs (12881B)


      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 -- Weigh allocation benchmarks for BOLT #7 gossip codecs.
     11 
     12 module Main where
     13 
     14 import qualified Data.ByteString as BS
     15 import Lightning.Protocol.BOLT1 (TlvStream, unsafeTlvStream)
     16 import Lightning.Protocol.BOLT7
     17 import Weigh
     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 feature bits.
    102 emptyFeatures :: FeatureBits
    103 emptyFeatures = featureBits BS.empty
    104 {-# NOINLINE emptyFeatures #-}
    105 
    106 -- | Empty TLV stream.
    107 emptyTlvs :: TlvStream
    108 emptyTlvs = unsafeTlvStream []
    109 {-# NOINLINE emptyTlvs #-}
    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 -- Message constructors --------------------------------------------------------
    126 
    127 -- | Construct ChannelAnnouncement message.
    128 mkChannelAnnouncement
    129   :: Signature -> Signature -> ChainHash -> ShortChannelId
    130   -> NodeId -> NodeId -> Point -> Point -> FeatureBits
    131   -> ChannelAnnouncement
    132 mkChannelAnnouncement !ns1 !ns2 !ch !scid !nid1 !nid2 !bk1 !bk2 !feat =
    133   ChannelAnnouncement
    134     { channelAnnNodeSig1     = ns1
    135     , channelAnnNodeSig2     = ns2
    136     , channelAnnBitcoinSig1  = ns1
    137     , channelAnnBitcoinSig2  = ns2
    138     , channelAnnFeatures     = feat
    139     , channelAnnChainHash    = ch
    140     , channelAnnShortChanId  = scid
    141     , channelAnnNodeId1      = nid1
    142     , channelAnnNodeId2      = nid2
    143     , channelAnnBitcoinKey1  = bk1
    144     , channelAnnBitcoinKey2  = bk2
    145     }
    146 
    147 -- | Construct NodeAnnouncement message.
    148 mkNodeAnnouncement
    149   :: Signature -> FeatureBits -> NodeId -> RgbColor -> Alias
    150   -> [Address] -> NodeAnnouncement
    151 mkNodeAnnouncement !sig !feat !nid !col !al !addrs = NodeAnnouncement
    152   { nodeAnnSignature = sig
    153   , nodeAnnFeatures  = feat
    154   , nodeAnnTimestamp = 1234567890
    155   , nodeAnnNodeId    = nid
    156   , nodeAnnRgbColor  = col
    157   , nodeAnnAlias     = al
    158   , nodeAnnAddresses = addrs
    159   }
    160 
    161 -- | Construct ChannelUpdate message.
    162 mkChannelUpdate :: Signature -> ChainHash -> ShortChannelId -> ChannelUpdate
    163 mkChannelUpdate !sig !ch !scid = ChannelUpdate
    164   { chanUpdateSignature       = sig
    165   , chanUpdateChainHash       = ch
    166   , chanUpdateShortChanId     = scid
    167   , chanUpdateTimestamp       = 1234567890
    168   , chanUpdateChanFlags       = ChannelFlags NodeOne Enabled
    169   , chanUpdateCltvExpDelta    = CltvExpiryDelta 144
    170   , chanUpdateHtlcMinMsat     = HtlcMinimumMsat 1000
    171   , chanUpdateFeeBaseMsat     = FeeBaseMsat 1000
    172   , chanUpdateFeeProportional = FeeProportionalMillionths 100
    173   , chanUpdateHtlcMaxMsat     = Just (HtlcMaximumMsat 1000000000)
    174   }
    175 
    176 -- | Construct AnnouncementSignatures message.
    177 mkAnnouncementSignatures
    178   :: ChannelId -> ShortChannelId -> Signature -> AnnouncementSignatures
    179 mkAnnouncementSignatures !cid !scid !sig = AnnouncementSignatures
    180   { annSigChannelId   = cid
    181   , annSigShortChanId = scid
    182   , annSigNodeSig     = sig
    183   , annSigBitcoinSig  = sig
    184   }
    185 
    186 -- | Construct GossipTimestampFilter message.
    187 mkGossipTimestampFilter :: ChainHash -> GossipTimestampFilter
    188 mkGossipTimestampFilter !ch = GossipTimestampFilter
    189   { gossipFilterChainHash      = ch
    190   , gossipFilterFirstTimestamp = 1609459200
    191   , gossipFilterTimestampRange = 86400
    192   }
    193 
    194 -- | Construct QueryChannelRange message.
    195 mkQueryChannelRange :: ChainHash -> TlvStream -> QueryChannelRange
    196 mkQueryChannelRange !ch !tlvs = QueryChannelRange
    197   { queryRangeChainHash  = ch
    198   , queryRangeFirstBlock = BlockHeight 700000
    199   , queryRangeNumBlocks  = BlockCount 10000
    200   , queryRangeTlvs       = tlvs
    201   }
    202 
    203 -- | Construct ReplyChannelRange message.
    204 mkReplyChannelRange :: ChainHash -> TlvStream -> ReplyChannelRange
    205 mkReplyChannelRange !ch !tlvs = ReplyChannelRange
    206   { replyRangeChainHash    = ch
    207   , replyRangeFirstBlock   = BlockHeight 700000
    208   , replyRangeNumBlocks    = BlockCount 10000
    209   , replyRangeSyncComplete = 1
    210   , replyRangeData         = encodeShortChannelIdList [testShortChannelId]
    211   , replyRangeTlvs         = tlvs
    212   }
    213 
    214 -- Pre-constructed messages ----------------------------------------------------
    215 
    216 -- | Test ChannelAnnouncement message.
    217 testChannelAnnouncement :: ChannelAnnouncement
    218 testChannelAnnouncement = mkChannelAnnouncement
    219   testSignature testSignature testChainHash testShortChannelId
    220   testNodeId2 testNodeId testPoint testPoint emptyFeatures
    221 {-# NOINLINE testChannelAnnouncement #-}
    222 
    223 -- | Encoded ChannelAnnouncement for decode benchmarks.
    224 encodedChannelAnnouncement :: BS.ByteString
    225 encodedChannelAnnouncement = encodeChannelAnnouncement testChannelAnnouncement
    226 {-# NOINLINE encodedChannelAnnouncement #-}
    227 
    228 -- | Test NodeAnnouncement message.
    229 testNodeAnnouncement :: NodeAnnouncement
    230 testNodeAnnouncement = mkNodeAnnouncement
    231   testSignature emptyFeatures testNodeId testRgbColor testAlias
    232   [AddrIPv4 testIPv4 9735]
    233 {-# NOINLINE testNodeAnnouncement #-}
    234 
    235 -- | Encoded NodeAnnouncement for decode benchmarks.
    236 encodedNodeAnnouncement :: BS.ByteString
    237 encodedNodeAnnouncement = case encodeNodeAnnouncement testNodeAnnouncement of
    238   Right bs -> bs
    239   Left _   -> error "encodedNodeAnnouncement: encode failed"
    240 {-# NOINLINE encodedNodeAnnouncement #-}
    241 
    242 -- | Test ChannelUpdate message.
    243 testChannelUpdate :: ChannelUpdate
    244 testChannelUpdate = mkChannelUpdate testSignature testChainHash testShortChannelId
    245 {-# NOINLINE testChannelUpdate #-}
    246 
    247 -- | Encoded ChannelUpdate for decode benchmarks.
    248 encodedChannelUpdate :: BS.ByteString
    249 encodedChannelUpdate = encodeChannelUpdate testChannelUpdate
    250 {-# NOINLINE encodedChannelUpdate #-}
    251 
    252 -- | Test AnnouncementSignatures message.
    253 testAnnouncementSignatures :: AnnouncementSignatures
    254 testAnnouncementSignatures =
    255   mkAnnouncementSignatures testChannelId testShortChannelId testSignature
    256 {-# NOINLINE testAnnouncementSignatures #-}
    257 
    258 -- | Encoded AnnouncementSignatures for decode benchmarks.
    259 encodedAnnouncementSignatures :: BS.ByteString
    260 encodedAnnouncementSignatures =
    261   encodeAnnouncementSignatures testAnnouncementSignatures
    262 {-# NOINLINE encodedAnnouncementSignatures #-}
    263 
    264 -- | Test GossipTimestampFilter message.
    265 testGossipTimestampFilter :: GossipTimestampFilter
    266 testGossipTimestampFilter = mkGossipTimestampFilter testChainHash
    267 {-# NOINLINE testGossipTimestampFilter #-}
    268 
    269 -- | Encoded GossipTimestampFilter for decode benchmarks.
    270 encodedGossipTimestampFilter :: BS.ByteString
    271 encodedGossipTimestampFilter =
    272   encodeGossipTimestampFilter testGossipTimestampFilter
    273 {-# NOINLINE encodedGossipTimestampFilter #-}
    274 
    275 -- | Test QueryChannelRange message.
    276 testQueryChannelRange :: QueryChannelRange
    277 testQueryChannelRange = mkQueryChannelRange testChainHash emptyTlvs
    278 {-# NOINLINE testQueryChannelRange #-}
    279 
    280 -- | Encoded QueryChannelRange for decode benchmarks.
    281 encodedQueryChannelRange :: BS.ByteString
    282 encodedQueryChannelRange = encodeQueryChannelRange testQueryChannelRange
    283 {-# NOINLINE encodedQueryChannelRange #-}
    284 
    285 -- | Test ReplyChannelRange message.
    286 testReplyChannelRange :: ReplyChannelRange
    287 testReplyChannelRange = mkReplyChannelRange testChainHash emptyTlvs
    288 {-# NOINLINE testReplyChannelRange #-}
    289 
    290 -- | Encoded ReplyChannelRange for decode benchmarks.
    291 encodedReplyChannelRange :: BS.ByteString
    292 encodedReplyChannelRange = case encodeReplyChannelRange testReplyChannelRange of
    293   Right bs -> bs
    294   Left _   -> error "encodedReplyChannelRange: encode failed"
    295 {-# NOINLINE encodedReplyChannelRange #-}
    296 
    297 -- Weigh benchmarks ------------------------------------------------------------
    298 
    299 main :: IO ()
    300 main = mainWith $ do
    301   wgroup "channel_announcement" $ do
    302     func "construct" (mkChannelAnnouncement
    303       testSignature testSignature testChainHash testShortChannelId
    304       testNodeId2 testNodeId testPoint testPoint) emptyFeatures
    305     func "encode" encodeChannelAnnouncement testChannelAnnouncement
    306     func "decode" decodeChannelAnnouncement encodedChannelAnnouncement
    307 
    308   wgroup "node_announcement" $ do
    309     func "construct" (mkNodeAnnouncement
    310       testSignature emptyFeatures testNodeId testRgbColor testAlias)
    311       [AddrIPv4 testIPv4 9735]
    312     func "encode" encodeNodeAnnouncement testNodeAnnouncement
    313     func "decode" decodeNodeAnnouncement encodedNodeAnnouncement
    314 
    315   wgroup "channel_update" $ do
    316     func "construct" (mkChannelUpdate testSignature testChainHash)
    317       testShortChannelId
    318     func "encode" encodeChannelUpdate testChannelUpdate
    319     func "decode" decodeChannelUpdate encodedChannelUpdate
    320 
    321   wgroup "announcement_signatures" $ do
    322     func "construct" (mkAnnouncementSignatures testChannelId testShortChannelId)
    323       testSignature
    324     func "encode" encodeAnnouncementSignatures testAnnouncementSignatures
    325     func "decode" decodeAnnouncementSignatures encodedAnnouncementSignatures
    326 
    327   wgroup "query_channel_range" $ do
    328     func "construct" (mkQueryChannelRange testChainHash) emptyTlvs
    329     func "encode" encodeQueryChannelRange testQueryChannelRange
    330     func "decode" decodeQueryChannelRange encodedQueryChannelRange
    331 
    332   wgroup "reply_channel_range" $ do
    333     func "construct" (mkReplyChannelRange testChainHash) emptyTlvs
    334     func "encode" encodeReplyChannelRange testReplyChannelRange
    335     func "decode" decodeReplyChannelRange encodedReplyChannelRange
    336 
    337   wgroup "gossip_timestamp_filter" $ do
    338     func "construct" mkGossipTimestampFilter testChainHash
    339     func "encode" encodeGossipTimestampFilter testGossipTimestampFilter
    340     func "decode" decodeGossipTimestampFilter encodedGossipTimestampFilter
    341 
    342   wgroup "scid_list" $ do
    343     func "encode (100)" encodeShortChannelIdList testScidList
    344     func "decode (100)" decodeShortChannelIdList encodedScidList
    345 
    346   wgroup "hash" $ do
    347     func "channelAnnouncementHash" channelAnnouncementHash
    348       encodedChannelAnnouncement
    349     func "nodeAnnouncementHash" nodeAnnouncementHash encodedNodeAnnouncement
    350     func "channelUpdateHash" channelUpdateHash encodedChannelUpdate
    351     func "channelUpdateChecksum" channelUpdateChecksum encodedChannelUpdate
    352 
    353   wgroup "validate" $ do
    354     func "channelAnnouncement" validateChannelAnnouncement testChannelAnnouncement
    355     func "nodeAnnouncement" validateNodeAnnouncement testNodeAnnouncement
    356     func "channelUpdate" validateChannelUpdate testChannelUpdate
    357     func "queryChannelRange" validateQueryChannelRange testQueryChannelRange
    358     func "replyChannelRange" validateReplyChannelRange testReplyChannelRange