bolt7

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

Validate.hs (3970B)


      1 {-# OPTIONS_HADDOCK hide #-}
      2 {-# LANGUAGE DeriveGeneric #-}
      3 
      4 -- |
      5 -- Module: Lightning.Protocol.BOLT7.Validate
      6 -- Copyright: (c) 2025 Jared Tobin
      7 -- License: MIT
      8 -- Maintainer: Jared Tobin <jared@ppad.tech>
      9 --
     10 -- Stateless checks of BOLT #7 messages.
     11 
     12 module Lightning.Protocol.BOLT7.Validate (
     13     ValidationError(..)
     14   , validate_channel_announcement
     15   , validate_node_announcement
     16   , validate_channel_update
     17   , validate_query_short_channel_ids
     18   , validate_query_channel_range
     19   , validate_reply_channel_range
     20   ) where
     21 
     22 import Control.DeepSeq (NFData)
     23 import Data.Word (Word64)
     24 import GHC.Generics (Generic)
     25 import Lightning.Protocol.BOLT1 (ShortChannelId)
     26 import Lightning.Protocol.BOLT7.Messages
     27 import Lightning.Protocol.BOLT7.Types
     28 
     29 -- | A rule a message breaks.
     30 data ValidationError
     31   = ValidateNodeIdOrdering
     32     -- ^ @node_id_1@ is not lexicographically less than @node_id_2@
     33   | ValidateMultipleDns
     34     -- ^ more than one DNS hostname descriptor
     35   | ValidateHtlcAmounts
     36     -- ^ @htlc_minimum_msat@ exceeds @htlc_maximum_msat@
     37   | ValidateZeroBlocks
     38     -- ^ @number_of_blocks@ is 0
     39   | ValidateBlockOverflow
     40     -- ^ the block range extends past block 2^32 - 1
     41   | ValidateScidNotAscending
     42     -- ^ short channel ids are not in strictly ascending order
     43   deriving (Eq, Show, Generic)
     44 
     45 instance NFData ValidationError
     46 
     47 -- | Check that @node_id_1@ is lexicographically less than @node_id_2@;
     48 --   a receiver must ignore the message otherwise.
     49 --
     50 --   Signatures and feature bits are not checked here; see
     51 --   'Lightning.Protocol.BOLT7.verify_channel_announcement' and
     52 --   ppad-bolt9's @validate_remote@.
     53 validate_channel_announcement
     54   :: ChannelAnnouncement -> Either ValidationError ()
     55 validate_channel_announcement m
     56   | ca_node_id_1 m < ca_node_id_2 m = Right ()
     57   | otherwise                       = Left ValidateNodeIdOrdering
     58 
     59 -- | Check that at most one DNS hostname is announced; a receiver must
     60 --   not forward the message otherwise.
     61 --
     62 --   Descriptors a receiver should merely ignore (Tor v2, port 0, a
     63 --   hostname that isn't ASCII) are left to
     64 --   'Lightning.Protocol.BOLT7.usable_addresses'.
     65 validate_node_announcement :: NodeAnnouncement -> Either ValidationError ()
     66 validate_node_announcement m
     67   | length [ () | AddrDNS _ _ <- na_addresses m ] > 1 =
     68       Left ValidateMultipleDns
     69   | otherwise = Right ()
     70 
     71 -- | Check that @htlc_minimum_msat@ does not exceed @htlc_maximum_msat@;
     72 --   a receiver should not route through the channel otherwise.
     73 validate_channel_update :: ChannelUpdate -> Either ValidationError ()
     74 validate_channel_update m
     75   | cu_htlc_minimum_msat m > cu_htlc_maximum_msat m = Left ValidateHtlcAmounts
     76   | otherwise = Right ()
     77 
     78 -- | Check that the short channel ids are in strictly ascending order, as
     79 --   their encoding requires.
     80 validate_query_short_channel_ids
     81   :: QueryShortChannelIds -> Either ValidationError ()
     82 validate_query_short_channel_ids = ascending . qsci_short_channel_ids
     83 
     84 -- | Check that @number_of_blocks@ is at least 1, as the sender must
     85 --   ensure, and that the range ends at or before block 2^32 - 1 (a
     86 --   library rule: such a range can't be represented).
     87 validate_query_channel_range
     88   :: QueryChannelRange -> Either ValidationError ()
     89 validate_query_channel_range m
     90   | num == 0                  = Left ValidateZeroBlocks
     91   | first + num > 0x100000000 = Left ValidateBlockOverflow
     92   | otherwise                 = Right ()
     93   where
     94     first = fromIntegral (qcr_first_blocknum m) :: Word64
     95     num   = fromIntegral (qcr_number_of_blocks m) :: Word64
     96 
     97 -- | Check that the short channel ids are in strictly ascending order, as
     98 --   their encoding requires.
     99 validate_reply_channel_range
    100   :: ReplyChannelRange -> Either ValidationError ()
    101 validate_reply_channel_range = ascending . rcr_short_channel_ids
    102 
    103 ascending :: [ShortChannelId] -> Either ValidationError ()
    104 ascending (a : rest@(b : _))
    105   | a < b     = ascending rest
    106   | otherwise = Left ValidateScidNotAscending
    107 ascending _ = Right ()