commit e0493e2d7e0233020fbf05dbf5af7e25e0d1fb8e
parent 02a53481ba70917cc169593e234fe2d8187cb026
Author: Jared Tobin <jared@jtobin.io>
Date: Sat, 10 Oct 2026 14:18:52 +0400
test: rewrite for the new API
Asserts every bolt07/extended-queries.json vector: the decodable ones
both ways against typed values, and the zlib ones as rejections.
Embeds six channel_announcements and thirteen channel_updates from
mainnet gossip, covering both lengths, every flag combination and an
update with htlc_minimum_msat above htlc_maximum_msat. Each decodes,
re-encodes exactly, hashes to an independently computed digest and
checksum, and verifies under the right key (and not the wrong one). A
signed node_announcement exercises every address descriptor type, an
unknown descriptor and an extension record.
Adds negative tests for every rejection rule, an independent CRC-32C
checked against the RFC 3720 vectors, and properties: round-trips of
generated messages, exact re-encoding of the embedded samples and of
mutations of them, and the flag accessors.
Diffstat:
| M | test/Main.hs | | | 2311 | +++++++++++++++++++++++++++++++++++++++---------------------------------------- |
| A | test/Vectors.hs | | | 376 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
2 files changed, 1521 insertions(+), 1166 deletions(-)
diff --git a/test/Main.hs b/test/Main.hs
@@ -2,1241 +2,1220 @@
module Main where
-import qualified Crypto.Curve.Secp256k1 as Secp256k1
+import Data.Bits ((.&.), (.|.), shiftR, xor)
import qualified Data.ByteString as BS
import qualified Data.ByteString.Base16 as B16
-import Data.Maybe (fromJust)
-import Data.Word (Word8, Word16, Word32)
+import Data.Word (Word8, Word16, Word32, Word64)
import Lightning.Protocol.BOLT1
- (TlvRecord(..), TlvStream, unTlvStream, unsafeTlvStream)
+ ( ChainHash, Point, ShortChannelId(..), Signature, TlvError(..)
+ , TlvRecord(..), TlvStream )
+import qualified Lightning.Protocol.BOLT1 as BOLT1
import Lightning.Protocol.BOLT7
-import Lightning.Protocol.BOLT7.CRC32C (crc32c)
+import qualified Lightning.Protocol.BOLT9 as BOLT9
import Test.Tasty
import Test.Tasty.HUnit
-import Test.Tasty.QuickCheck
+import Test.Tasty.QuickCheck hiding ((.&.))
+import Vectors
main :: IO ()
main = defaultMain $ testGroup "ppad-bolt7" [
- type_tests
- , channel_announcement_tests
+ field_tests
+ , spec_vector_tests
+ , mainnet_tests
, node_announcement_tests
, channel_update_tests
- , announcement_signatures_tests
, query_tests
- , scid_list_tests
- , hash_tests
+ , other_message_tests
+ , encode_error_tests
+ , dispatch_tests
, validation_tests
- , error_tests
- , mainnet_tests
+ , checksum_tests
, property_tests
]
--- Test data helpers -----------------------------------------------------------
-
--- | Create a valid ChainHash (32 bytes).
-testChainHash :: ChainHash
-testChainHash = fromJust $ chainHash (BS.replicate 32 0x01)
-
--- | Create a valid ShortChannelId (8 bytes).
-testShortChannelId :: ShortChannelId
-testShortChannelId = fromJust $ scidFromBytes (BS.replicate 8 0xab)
-
--- | Create a valid ChannelId (32 bytes).
-testChannelId :: ChannelId
-testChannelId = fromJust $ channelId (BS.replicate 32 0xcd)
-
--- | Create a valid Signature (64 bytes).
-testSignature :: Signature
-testSignature = fromJust $ signature (BS.replicate 64 0xee)
-
--- | Create a valid Point (33 bytes).
-testPoint :: Point
-testPoint = fromJust $ point (BS.pack $ 0x02 : replicate 32 0xff)
-
--- | Create a valid NodeId (33 bytes).
-testNodeId :: NodeId
-testNodeId = fromJust $ nodeId (BS.pack $ 0x03 : replicate 32 0xaa)
-
--- | Create a second valid NodeId (33 bytes).
-testNodeId2 :: NodeId
-testNodeId2 = fromJust $ nodeId (BS.pack $ 0x02 : replicate 32 0xbb)
-
--- | Create a valid RgbColor (3 bytes).
-testRgbColor :: RgbColor
-testRgbColor = fromJust $ rgbColor (BS.pack [0xff, 0x00, 0x00])
-
--- | Create a valid Alias (32 bytes).
-testAlias :: Alias
-testAlias = fromJust $ alias (BS.pack $ replicate 32 0x00)
-
--- | Empty TLV stream for messages.
-emptyTlvs :: TlvStream
-emptyTlvs = unsafeTlvStream []
-
--- | Empty feature bits.
-emptyFeatures :: FeatureBits
-emptyFeatures = featureBits BS.empty
-
--- | Total ShortChannelId constructor for test fixtures.
-mkScid :: Word32 -> Word32 -> Word16 -> ShortChannelId
-mkScid b t o = case shortChannelId b t o of
- Just s -> s
- Nothing -> error "mkScid: invalid test fixture"
-
--- | A channel_update fixture.
-testChannelUpdate :: ChannelUpdate
-testChannelUpdate = ChannelUpdate
- { chanUpdateSignature = testSignature
- , chanUpdateChainHash = testChainHash
- , chanUpdateShortChanId = testShortChannelId
- , chanUpdateTimestamp = 1234567890
- , chanUpdateChanFlags = ChannelFlags
- { cfDirection = NodeOne, cfStatus = Enabled }
- , chanUpdateCltvExpDelta = CltvExpiryDelta 144
- , chanUpdateHtlcMinMsat = HtlcMinimumMsat 1000
- , chanUpdateFeeBaseMsat = FeeBaseMsat 1000
- , chanUpdateFeeProportional = FeeProportionalMillionths 100
- , chanUpdateHtlcMaxMsat = HtlcMaximumMsat 1000000000
- }
+-- helpers --------------------------------------------------------------------
--- | Unwrap a Maybe, failing the test on Nothing.
-expectJust :: String -> Maybe a -> IO a
-expectJust what = maybe (assertFailure ("unexpected Nothing: " ++ what)) pure
-
--- | A node_announcement fixture with the given addresses.
-testNodeAnn :: [Address] -> NodeAnnouncement
-testNodeAnn addrs = NodeAnnouncement
- { nodeAnnSignature = testSignature
- , nodeAnnFeatures = emptyFeatures
- , nodeAnnTimestamp = 1234567890
- , nodeAnnNodeId = testNodeId
- , nodeAnnRgbColor = testRgbColor
- , nodeAnnAlias = testAlias
- , nodeAnnAddresses = addrs
- }
+must :: String -> Maybe a -> IO a
+must what = maybe (assertFailure ("unexpected Nothing: " ++ what)) pure
--- | A channel_announcement fixture with the given features.
-testChannelAnn :: FeatureBits -> ChannelAnnouncement
-testChannelAnn feats = ChannelAnnouncement
- { channelAnnNodeSig1 = testSignature
- , channelAnnNodeSig2 = testSignature
- , channelAnnBitcoinSig1 = testSignature
- , channelAnnBitcoinSig2 = testSignature
- , channelAnnFeatures = feats
- , channelAnnChainHash = testChainHash
- , channelAnnShortChanId = testShortChannelId
- , channelAnnNodeId1 = testNodeId2
- , channelAnnNodeId2 = testNodeId
- , channelAnnBitcoinKey1 = testPoint
- , channelAnnBitcoinKey2 = testPoint
- }
+expectRight :: Show e => String -> Either e a -> IO a
+expectRight what =
+ either (\e -> assertFailure (what ++ " failed: " ++ show e)) pure
+
+hex :: BS.ByteString -> IO BS.ByteString
+hex h = must ("hex " ++ show h) (B16.decode h)
+
+-- total helpers for pure contexts (properties)
+chain_hash_of :: Word8 -> Maybe ChainHash
+chain_hash_of w = BOLT1.chain_hash (BS.replicate 32 w)
+
+big_endian :: Int -> Word64 -> BS.ByteString
+big_endian n w =
+ BS.pack [ fromIntegral (w `shiftR` (8 * i)) | i <- [n - 1, n - 2 .. 0] ]
--- | Replace the byte at an offset (no-op past the end).
-setByte :: Int -> Word8 -> BS.ByteString -> BS.ByteString
-setByte i w bs
+u16be :: Int -> BS.ByteString
+u16be = big_endian 2 . fromIntegral
+
+-- replace the byte at an offset (no-op past the end)
+set_byte :: Int -> Word8 -> BS.ByteString -> BS.ByteString
+set_byte i w bs
| i < 0 || i >= BS.length bs = bs
| otherwise = BS.take i bs <> BS.singleton w <> BS.drop (i + 1) bs
--- Type Tests ------------------------------------------------------------------
-
-type_tests :: TestTree
-type_tests = testGroup "Types" [
- testGroup "ShortChannelId" [
- testCase "scidBlockHeight" $ do
- -- 8 bytes: block=0x123456, tx=0x789abc, output=0xdef0
- let scid = fromJust $ scidFromBytes (BS.pack
- [0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0])
- scidBlockHeight scid @?= 0x123456
- , testCase "scidTxIndex" $ do
- let scid = fromJust $ scidFromBytes (BS.pack
- [0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0])
- scidTxIndex scid @?= 0x789abc
- , testCase "scidOutputIndex" $ do
- let scid = fromJust $ scidFromBytes (BS.pack
- [0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0])
- scidOutputIndex scid @?= 0xdef0
- , testCase "shortChannelId roundtrip" $ do
- let scid = fromJust $ shortChannelId 539268 845 1
- scidBlockHeight scid @?= 539268
- scidTxIndex scid @?= 845
- scidOutputIndex scid @?= 1
- , testCase "formatScid" $ do
- let scid = fromJust $ shortChannelId 539268 845 1
- formatScid scid @?= "539268x845x1"
- , testCase "formatScid zero values" $ do
- let scid = fromJust $ shortChannelId 0 0 0
- formatScid scid @?= "0x0x0"
- ]
- , testGroup "Smart constructors" [
- testCase "chainHash rejects wrong length" $ do
- chainHash (BS.replicate 31 0x00) @?= Nothing
- chainHash (BS.replicate 33 0x00) @?= Nothing
- , testCase "scidFromBytes rejects wrong length" $ do
- scidFromBytes (BS.replicate 7 0x00) @?= Nothing
- scidFromBytes (BS.replicate 9 0x00) @?= Nothing
- , testCase "signature rejects wrong length" $ do
- signature (BS.replicate 63 0x00) @?= Nothing
- signature (BS.replicate 65 0x00) @?= Nothing
- , testCase "point rejects wrong length" $ do
- point (BS.replicate 32 0x00) @?= Nothing
- point (BS.replicate 34 0x00) @?= Nothing
- ]
- , testGroup "Constants" [
- testCase "mainnetChainHash has correct length" $ do
- BS.length (unChainHash mainnetChainHash) @?= 32
- ]
- , testGroup "NodeId ordering" [
- testCase "NodeId Ord is lexicographic" $ do
- let n1 = fromJust $ nodeId (BS.pack $ 0x02 : replicate 32 0x00)
- n2 = fromJust $ nodeId (BS.pack $ 0x03 : replicate 32 0x00)
- n1 < n2 @?= True
- n2 < n1 @?= False
- ]
- ]
+-- | Fixture values, built with the smart constructors.
+data Fx = Fx
+ { fx_chain :: ChainHash
+ , fx_sig :: Signature
+ , fx_node_1 :: Point
+ , fx_node_2 :: Point
+ , fx_cid :: BOLT1.ChannelId
+ , fx_scid :: ShortChannelId
+ , fx_alias :: Alias
+ , fx_scids :: [ShortChannelId]
+ }
+
+fixtures :: IO Fx
+fixtures = do
+ ch <- must "chain_hash" (chain_hash_of 0x01)
+ sg <- must "signature" (BOLT1.signature (BS.replicate 64 0xee))
+ n1 <- must "point" (BOLT1.point (BS.cons 0x02 (BS.replicate 32 0xbb)))
+ n2 <- must "point" (BOLT1.point (BS.cons 0x03 (BS.replicate 32 0xaa)))
+ ci <- must "channel_id" (BOLT1.channel_id (BS.replicate 32 0xcd))
+ sc <- must "short_channel_id" (BOLT1.short_channel_id 539268 845 1)
+ al <- must "alias" (alias (BS.replicate 32 0))
+ ss <- must "short_channel_ids" $ traverse
+ (\(b, t, o) -> BOLT1.short_channel_id b t o)
+ [(100000, 1, 0), (200000, 2, 1), (300000, 3, 2)]
+ pure Fx
+ { fx_chain = ch
+ , fx_sig = sg
+ , fx_node_1 = n1
+ , fx_node_2 = n2
+ , fx_cid = ci
+ , fx_scid = sc
+ , fx_alias = al
+ , fx_scids = ss
+ }
+
+msat :: Word64 -> IO BOLT1.MilliSatoshi
+msat = must "milli_satoshi" . BOLT1.milli_satoshi
+
+update_fixture :: Fx -> IO ChannelUpdate
+update_fixture fx = do
+ hmin <- msat 1000
+ hmax <- msat 1000000000
+ pure ChannelUpdate
+ { cu_signature = fx_sig fx
+ , cu_chain_hash = fx_chain fx
+ , cu_short_channel_id = fx_scid fx
+ , cu_timestamp = 1234567890
+ , cu_message_flags = message_flags Forward
+ , cu_channel_flags = channel_flags NodeOne Enabled
+ , cu_cltv_expiry_delta = 144
+ , cu_htlc_minimum_msat = hmin
+ , cu_fee_base_msat = 1000
+ , cu_fee_proportional_millionths = 100
+ , cu_htlc_maximum_msat = hmax
+ , cu_tlvs = BOLT1.empty_tlv_stream
+ }
+
+announcement_fixture :: Fx -> ChannelAnnouncement
+announcement_fixture fx = ChannelAnnouncement
+ { ca_node_signature_1 = fx_sig fx
+ , ca_node_signature_2 = fx_sig fx
+ , ca_bitcoin_signature_1 = fx_sig fx
+ , ca_bitcoin_signature_2 = fx_sig fx
+ , ca_features = BOLT9.empty
+ , ca_chain_hash = fx_chain fx
+ , ca_short_channel_id = fx_scid fx
+ , ca_node_id_1 = fx_node_1 fx
+ , ca_node_id_2 = fx_node_2 fx
+ , ca_bitcoin_key_1 = fx_node_1 fx
+ , ca_bitcoin_key_2 = fx_node_2 fx
+ , ca_tlvs = BOLT1.empty_tlv_stream
+ }
--- Channel Announcement Tests --------------------------------------------------
-
-channel_announcement_tests :: TestTree
-channel_announcement_tests = testGroup "ChannelAnnouncement" [
- testCase "encode/decode roundtrip" $ do
- let msg = ChannelAnnouncement
- { channelAnnNodeSig1 = testSignature
- , channelAnnNodeSig2 = testSignature
- , channelAnnBitcoinSig1 = testSignature
- , channelAnnBitcoinSig2 = testSignature
- , channelAnnFeatures = emptyFeatures
- , channelAnnChainHash = testChainHash
- , channelAnnShortChanId = testShortChannelId
- , channelAnnNodeId1 = testNodeId
- , channelAnnNodeId2 = testNodeId2
- , channelAnnBitcoinKey1 = testPoint
- , channelAnnBitcoinKey2 = testPoint
- }
- case encodeChannelAnnouncement msg of
- Left e -> assertFailure $ "encode failed: " ++ show e
- Right encoded -> case decodeChannelAnnouncement encoded of
- Right (decoded, _) -> decoded @?= msg
- Left e -> assertFailure $ "decode failed: " ++ show e
+node_fixture :: Fx -> [Address] -> NodeAnnouncement
+node_fixture fx addrs = NodeAnnouncement
+ { na_signature = fx_sig fx
+ , na_features = BOLT9.empty
+ , na_timestamp = 1234567890
+ , na_node_id = fx_node_2 fx
+ , na_rgb_color = RgbColor 0xff 0x00 0x00
+ , na_alias = fx_alias fx
+ , na_addresses = addrs
+ , na_unknown_addresses = BS.empty
+ , na_tlvs = BOLT1.empty_tlv_stream
+ }
+
+tlvs_of :: [TlvRecord] -> IO TlvStream
+tlvs_of = must "tlv_stream" . BOLT1.tlv_stream
+
+-- field types ----------------------------------------------------------------
+
+field_tests :: TestTree
+field_tests = testGroup "field types" [
+ testCase "format_short_channel_id" $ do
+ let fmt b t o =
+ fmap format_short_channel_id (BOLT1.short_channel_id b t o)
+ fmt 539268 845 1 @?= Just "539268x845x1"
+ fmt 0 0 0 @?= Just "0x0x0"
+ fmt 0xffffff 0xffffff 0xffff @?= Just "16777215x16777215x65535"
+ , testCase "message_flags" $ do
+ message_flags Forward @?= MessageFlags 0x01
+ message_flags DontForward @?= MessageFlags 0x03
+ map (message_flags_forwarding . MessageFlags) [0, 1, 2, 3, 0xfd]
+ @?= [Forward, Forward, DontForward, DontForward, Forward]
+ map (has_must_be_one . MessageFlags) [0, 1, 2, 0xfe]
+ @?= [False, True, False, False]
+ , testCase "channel_flags" $ do
+ [ channel_flags d s | d <- [NodeOne, NodeTwo], s <- [Enabled, Disabled] ]
+ @?= map ChannelFlags [0, 2, 1, 3]
+ map (channel_flags_direction . ChannelFlags) [0x04, 0x05, 0xfe]
+ @?= [NodeOne, NodeTwo, NodeOne]
+ map (channel_flags_status . ChannelFlags) [0x04, 0x06, 0xfd]
+ @?= [Enabled, Disabled, Enabled]
+ , testCase "query_flags" $ do
+ query_flags [] @?= QueryFlags 0
+ query_flags [minBound .. maxBound] @?= QueryFlags 31
+ query_flags [WantNodeAnnouncement2, WantChannelUpdate1] @?= QueryFlags 18
+ map (`has_query_flag` QueryFlags 0x25) [minBound .. maxBound]
+ @?= [True, False, True, False, False]
+ , testCase "query_option" $ do
+ query_option [WantChecksums] @?= QueryOption 2
+ map (`has_query_option` QueryOption 0x05) [minBound .. maxBound]
+ @?= [True, False]
+ , testCase "fixed-size constructors check lengths" $ do
+ let lens mk = [ mk (BS.replicate n 0) /= Nothing | n <- [0 .. 40] ]
+ only n = [ k == n | k <- [0 .. 40 :: Int] ]
+ lens alias @?= only 32
+ lens ipv4_addr @?= only 4
+ lens ipv6_addr @?= only 16
+ lens tor_v2_addr @?= only 12
+ lens tor_v3_addr @?= only 35
+ , testCase "hostname" $ do
+ fmap un_hostname (hostname "ln.example.com") @?= Just "ln.example.com"
+ fmap un_hostname (hostname "") @?= Just ""
+ fmap (BS.length . un_hostname) (hostname (BS.replicate 255 0x61))
+ @?= Just 255
+ hostname (BS.replicate 256 0x61) @?= Nothing
+ let ascii h = fmap is_ascii_hostname (hostname h)
+ ascii "xn--caf-dma" @?= Just True
+ ascii "caf\233" @?= Just False
+ ascii "\0\255" @?= Just False
+ ascii "" @?= Just False
+ , testCase "usable_addresses" $ do
+ v4 <- must "ipv4" (ipv4_addr "\127\0\0\1")
+ v6 <- must "ipv6" (ipv6_addr (BS.replicate 16 1))
+ t2 <- must "tor v2" (tor_v2_addr (BS.replicate 12 0xaa))
+ t3 <- must "tor v3" (tor_v3_addr (BS.replicate 35 0xbb))
+ h1 <- must "hostname" (hostname "a.example.com")
+ h2 <- must "hostname" (hostname "b.example.com")
+ bad <- must "hostname" (hostname "caf\233")
+ usable_addresses
+ [ AddrIPv4 v4 9735, AddrTorV2 t2, AddrIPv6 v6 0, AddrTorV3 t3 9735
+ , AddrDNS h1 9735, AddrDNS h2 9735, AddrIPv6 v6 9735 ]
+ @?= [ AddrIPv4 v4 9735, AddrTorV3 t3 9735, AddrDNS h1 9735
+ , AddrIPv6 v6 9735 ]
+ -- a hostname that is ignored still counts as the first
+ usable_addresses [AddrDNS bad 9735, AddrDNS h1 9735] @?= []
+ usable_addresses [AddrDNS h1 0, AddrDNS h2 9735] @?= []
]
--- Node Announcement Tests -----------------------------------------------------
+-- extended-queries.json ------------------------------------------------------
+
+-- the vectors' chain hash (regtest)
+regtest :: IO ChainHash
+regtest = hex "0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206"
+ >>= must "chain_hash" . BOLT1.chain_hash
+
+scids :: [(Word32, Word32, Word16)] -> IO [ShortChannelId]
+scids = must "short_channel_id"
+ . traverse (\(b, t, o) -> BOLT1.short_channel_id b t o)
+
+-- decode a vector, compare with the expected message, and re-encode the
+-- expected message
+known_answer :: BS.ByteString -> Message -> Assertion
+known_answer h expected = do
+ wire <- hex h
+ (t, payload) <- expectRight "decode_envelope" (BOLT1.decode_envelope wire)
+ decode_message t payload @?= Right expected
+ message_type expected @?= t
+ encode_message expected @?= Right payload
+
+rejected :: BS.ByteString -> DecodeError -> Assertion
+rejected h err = do
+ wire <- hex h
+ (t, payload) <- expectRight "decode_envelope" (BOLT1.decode_envelope wire)
+ decode_message t payload @?= Left err
+
+spec_vector_tests :: TestTree
+spec_vector_tests = testGroup "bolt07/extended-queries.json" $
+ case query_vectors of
+ [v0, v1, v2, v3, v4, v5, v6, v7, v8, v9] -> [
+ testCase "0: query_channel_range" $ do
+ ch <- regtest
+ known_answer v0 $ MsgQueryChannelRange
+ (QueryChannelRange ch 100000 1500 Nothing BOLT1.empty_tlv_stream)
+ , testCase "1: query_channel_range with query_option" $ do
+ ch <- regtest
+ known_answer v1 $ MsgQueryChannelRange $ QueryChannelRange ch
+ 35000 100 (Just (query_option [WantTimestamps, WantChecksums]))
+ BOLT1.empty_tlv_stream
+ , testCase "2: reply_channel_range" $ do
+ ch <- regtest
+ ss <- scids [(0, 0, 142), (0, 0, 15465), (0, 69, 42692)]
+ known_answer v2 $ MsgReplyChannelRange
+ (ReplyChannelRange ch 756230 1500 True ss Nothing Nothing
+ BOLT1.empty_tlv_stream)
+ , testCase "3: reply_channel_range, zlib (rejected)" $
+ rejected v3 (DecodeUnknownEncoding 1)
+ , testCase "4: reply_channel_range with timestamps and checksums" $ do
+ ch <- regtest
+ ss <- scids [(0, 0, 12355), (0, 7, 30934), (0, 70, 57793)]
+ known_answer v4 $ MsgReplyChannelRange $
+ ReplyChannelRange ch 122334 1500 True ss
+ (Just [ ChannelUpdateTimestamps 164545 948165
+ , ChannelUpdateTimestamps 489645 4786864
+ , ChannelUpdateTimestamps 46456 9788415 ])
+ (Just [ ChannelUpdateChecksums 1111 2222
+ , ChannelUpdateChecksums 3333 4444
+ , ChannelUpdateChecksums 5555 6666 ])
+ BOLT1.empty_tlv_stream
+ , testCase "5: reply_channel_range, zlib (rejected)" $
+ rejected v5 (DecodeUnknownEncoding 1)
+ , testCase "6: query_short_channel_ids" $ do
+ ch <- regtest
+ ss <- scids [(0, 0, 142), (0, 0, 15465), (0, 69, 42692)]
+ known_answer v6 $ MsgQueryShortChannelIds
+ (QueryShortChannelIds ch ss Nothing BOLT1.empty_tlv_stream)
+ , testCase "7: query_short_channel_ids, zlib (rejected)" $
+ rejected v7 (DecodeUnknownEncoding 1)
+ , testCase "8: query_short_channel_ids, zlib query_flags (rejected)" $
+ rejected v8 (DecodeUnknownEncoding 1)
+ , testCase "9: query_short_channel_ids, zlib (rejected)" $
+ rejected v9 (DecodeUnknownEncoding 1)
+ ]
+ _ -> [testCase "vector count" (assertFailure "expected 10 vectors")]
+
+-- mainnet gossip -------------------------------------------------------------
+
+decoded_announcements :: IO [(BS.ByteString, ChannelAnnouncement)]
+decoded_announcements = traverse go mainnet_announcements
+ where
+ go v = do
+ p <- hex (av_payload v)
+ a <- expectRight "decode_channel_announcement"
+ (decode_channel_announcement p)
+ pure (p, a)
+
+-- the key of the node that signed an update
+signer :: ChannelAnnouncement -> ChannelUpdate -> Point
+signer a u = case channel_flags_direction (cu_channel_flags u) of
+ NodeOne -> ca_node_id_1 a
+ NodeTwo -> ca_node_id_2 a
+
+other :: ChannelAnnouncement -> ChannelUpdate -> Point
+other a u = case channel_flags_direction (cu_channel_flags u) of
+ NodeOne -> ca_node_id_2 a
+ NodeTwo -> ca_node_id_1 a
-node_announcement_tests :: TestTree
-node_announcement_tests = testGroup "NodeAnnouncement" [
- testCase "encode/decode roundtrip with no addresses" $ do
- let msg = NodeAnnouncement
- { nodeAnnSignature = testSignature
- , nodeAnnFeatures = emptyFeatures
- , nodeAnnTimestamp = 1234567890
- , nodeAnnNodeId = testNodeId
- , nodeAnnRgbColor = testRgbColor
- , nodeAnnAlias = testAlias
- , nodeAnnAddresses = []
- }
- case encodeNodeAnnouncement msg of
- Left e -> assertFailure $ "encode failed: " ++ show e
- Right encoded -> case decodeNodeAnnouncement encoded of
- Right (decoded, _) -> decoded @?= msg
- Left e -> assertFailure $ "decode failed: " ++ show e
- , testCase "encode/decode roundtrip with IPv4 address" $ do
- let ipv4 = fromJust $ ipv4Addr (BS.pack [127, 0, 0, 1])
- msg = NodeAnnouncement
- { nodeAnnSignature = testSignature
- , nodeAnnFeatures = emptyFeatures
- , nodeAnnTimestamp = 1234567890
- , nodeAnnNodeId = testNodeId
- , nodeAnnRgbColor = testRgbColor
- , nodeAnnAlias = testAlias
- , nodeAnnAddresses = [AddrIPv4 ipv4 9735]
- }
- case encodeNodeAnnouncement msg of
- Left e -> assertFailure $ "encode failed: " ++ show e
- Right encoded -> case decodeNodeAnnouncement encoded of
- Right (decoded, _) -> decoded @?= msg
- Left e -> assertFailure $ "decode failed: " ++ show e
- , testGroup "address descriptors" [
- testCase "skips deprecated Tor v2 descriptors" $ do
- v4 <- addrV4
- v6 <- addrV6
- decodeAddrs (descV4 <> descTorV2 <> descV6) @?= Right [v4, v6]
- , testCase "stops at the first unknown type" $ do
- v4 <- addrV4
- let wire = descV4 <> BS.pack [6, 1, 2, 3] <> descV6
- decodeAddrs wire @?= Right [v4]
- -- the ignored bytes are still consumed as part of addrlen
- fmap snd (decodeNodeAnnouncement (nodeAnnWire wire))
- @?= Right BS.empty
- , testCase "stops at a type-0 descriptor" $
- decodeAddrs (BS.singleton 0 <> descV4) @?= Right []
- , testCase "skips descriptors with port 0" $ do
- v6 <- addrV6
- let v4port0 = BS.pack [1, 127, 0, 0, 1, 0, 0]
- decodeAddrs (v4port0 <> descV6) @?= Right [v6]
- , testCase "skips empty and non-ASCII hostnames" $ do
- h <- expectJust "hostname" (hostname "ln.example.com")
- let wire = descDns "" <> descDns "\xff\&ab"
- <> descDns "ln.example.com"
- decodeAddrs wire @?= Right [AddrDNS h 9735]
- , testCase "rejects a truncated descriptor of a known type" $ do
- decodeAddrs (BS.pack [1, 127, 0, 0])
- @?= Left DecodeInsufficientBytes
- decodeAddrs (BS.pack [3, 1, 2, 3])
- @?= Left DecodeInsufficientBytes
- decodeAddrs (BS.pack [5, 4, 0x61])
- @?= Left DecodeInsufficientBytes
- , testCase "hostname rejects non-ASCII bytes" $ do
- hostname "\0\255" @?= Nothing
- hostname "caf\233" @?= Nothing
- fmap getHostname (hostname "xn--caf-dma") @?= Just "xn--caf-dma"
- ]
+mainnet_tests :: TestTree
+mainnet_tests = testGroup "mainnet gossip" [
+ testCase "channel_announcements: decode, re-encode, hash, verify" $ do
+ anns <- decoded_announcements
+ length anns @?= 6
+ mapM_ (\(v, (p, a)) -> do
+ encode_channel_announcement a @?= Right p
+ d <- hex (av_digest v)
+ channel_announcement_hash a @?= Right d
+ assertBool "signatures verify" (verify_channel_announcement a)
+ validate_channel_announcement a @?= Right ())
+ (zip mainnet_announcements anns)
+ , testCase "channel_announcement fields" $ do
+ anns <- decoded_announcements
+ (_, a) <- must "first announcement" (safe_head anns)
+ mainnet <- hex
+ "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000"
+ n1 <- hex
+ "02bb5e47ee26aca1654ce092d9d06eac15d953210c737f2220e2d7e54c4f108677"
+ n2 <- hex
+ "030b828fd1e42549fbc2f1d0960ec3663362c41777d3d87fe1b8e1d4bf063abcaa"
+ format_short_channel_id (ca_short_channel_id a) @?= "850052x2992x1"
+ BOLT1.un_chain_hash (ca_chain_hash a) @?= mainnet
+ BOLT9.render (ca_features a) @?= BS.empty
+ BOLT1.un_point (ca_node_id_1 a) @?= n1
+ BOLT1.un_point (ca_node_id_2 a) @?= n2
+ BOLT1.un_tlv_stream (ca_tlvs a) @?= []
+ , testCase "channel_announcement: tampering breaks verification" $ do
+ anns <- decoded_announcements
+ (p, a) <- must "first announcement" (safe_head anns)
+ -- change the short channel id (signed)
+ let p' = set_byte 295 0xff p
+ a' <- expectRight "decode" (decode_channel_announcement p')
+ assertBool "tampered" (not (verify_channel_announcement a'))
+ let swapped = a { ca_node_signature_1 = ca_node_signature_2 a
+ , ca_node_signature_2 = ca_node_signature_1 a }
+ assertBool "swapped" (not (verify_channel_announcement swapped))
+ , testCase "channel_updates: decode, re-encode, hash, checksum, verify" $ do
+ anns <- decoded_announcements
+ length mainnet_updates @?= 13
+ mapM_ (\v -> do
+ p <- hex (uv_payload v)
+ u <- expectRight "decode_channel_update" (decode_channel_update p)
+ (_, a) <- must "announcement" (safe_index anns (uv_ann v))
+ cu_short_channel_id u @?= ca_short_channel_id a
+ encode_channel_update u @?= p
+ d <- hex (uv_digest v)
+ channel_update_hash u @?= d
+ channel_update_checksum u @?= uv_checksum v
+ assertBool "verifies under its signer"
+ (verify_channel_update (signer a u) u)
+ assertBool "fails under the other node"
+ (not (verify_channel_update (other a u) u)))
+ mainnet_updates
+ , testCase "channel_update fields (136 bytes)" $ do
+ u <- update_vector 1
+ BS.length (encode_channel_update u) @?= 136
+ format_short_channel_id (cu_short_channel_id u) @?= "850052x2992x1"
+ cu_timestamp u @?= 1776493010
+ cu_message_flags u @?= MessageFlags 0x01
+ cu_channel_flags u @?= channel_flags NodeTwo Disabled
+ cu_cltv_expiry_delta u @?= 80
+ BOLT1.un_milli_satoshi (cu_htlc_minimum_msat u) @?= 1000
+ cu_fee_base_msat u @?= 1000
+ cu_fee_proportional_millionths u @?= 800
+ BOLT1.un_milli_satoshi (cu_htlc_maximum_msat u) @?= 2970000000
+ BOLT1.un_tlv_stream (cu_tlvs u) @?= []
+ , testCase "channel_update fields (148 bytes, inbound-fee extension)" $ do
+ u <- update_vector 0
+ ext <- hex "00000000ffffffed"
+ cu_timestamp u @?= 1779047542
+ cu_channel_flags u @?= channel_flags NodeOne Enabled
+ cu_cltv_expiry_delta u @?= 100
+ cu_fee_base_msat u @?= 0
+ cu_fee_proportional_millionths u @?= 5000
+ BOLT1.un_milli_satoshi (cu_htlc_maximum_msat u) @?= 2970000000
+ BOLT1.un_tlv_stream (cu_tlvs u) @?= [TlvRecord 55555 ext]
+ , testCase "a real update with htlc_minimum_msat > htlc_maximum_msat" $ do
+ u <- update_vector 8
+ validate_channel_update u @?= Left ValidateHtlcAmounts
+ , testCase "dropping the extension breaks verification" $ do
+ anns <- decoded_announcements
+ (_, a) <- must "announcement" (safe_head anns)
+ u <- update_vector 0
+ let u' = u { cu_tlvs = BOLT1.empty_tlv_stream }
+ assertBool "with" (verify_channel_update (signer a u) u)
+ assertBool "without" (not (verify_channel_update (signer a u) u'))
]
+ where
+ update_vector i = do
+ v <- must "update vector" (safe_index mainnet_updates i)
+ p <- hex (uv_payload v)
+ expectRight "decode_channel_update" (decode_channel_update p)
--- | node_announcement payload carrying the given address bytes.
-nodeAnnWire :: BS.ByteString -> BS.ByteString
-nodeAnnWire addrs = mconcat
+safe_head :: [a] -> Maybe a
+safe_head (x : _) = Just x
+safe_head [] = Nothing
+
+safe_index :: [a] -> Int -> Maybe a
+safe_index xs i
+ | i < 0 = Nothing
+ | otherwise = safe_head (drop i xs)
+
+-- node_announcement ----------------------------------------------------------
+
+-- a node_announcement payload carrying the given address bytes
+node_wire :: Fx -> BS.ByteString -> BS.ByteString
+node_wire fx addrs = mconcat
[ BS.replicate 64 0xee -- signature
- , BS.pack [0, 0] -- flen
+ , u16be 0 -- flen
, BS.pack [0x49, 0x96, 0x02, 0xd2] -- timestamp
- , getNodeId testNodeId
- , getRgbColor testRgbColor
- , getAlias testAlias
+ , BOLT1.un_point (fx_node_2 fx)
+ , BS.pack [0xff, 0x00, 0x00]
+ , BS.replicate 32 0
, u16be (BS.length addrs)
, addrs
]
--- | Decode the addresses of a node_announcement built by 'nodeAnnWire'.
-decodeAddrs :: BS.ByteString -> Either DecodeError [Address]
-decodeAddrs = fmap (nodeAnnAddresses . fst) . decodeNodeAnnouncement
- . nodeAnnWire
+-- address descriptors (port 9735)
+desc_v4, desc_v6, desc_tor_v2, desc_tor_v3 :: BS.ByteString
+desc_v4 = BS.pack [1, 127, 0, 0, 1, 0x26, 0x07]
+desc_v6 = BS.pack (2 : replicate 15 0 ++ [1, 0x26, 0x07])
+desc_tor_v2 = BS.cons 3 (BS.replicate 12 0xaa)
+desc_tor_v3 = BS.cons 4 (BS.replicate 35 0xbb) <> u16be 9735
+
+desc_dns :: BS.ByteString -> BS.ByteString
+desc_dns h = BS.pack [5, fromIntegral (BS.length h)] <> h <> u16be 9735
+
+-- decode the addresses of a node_announcement built by node_wire, and
+-- check that it re-encodes exactly
+addrs_of
+ :: Fx -> BS.ByteString -> IO (Either DecodeError ([Address], BS.ByteString))
+addrs_of fx bytes = do
+ let wire = node_wire fx bytes
+ case decode_node_announcement wire of
+ Left e -> pure (Left e)
+ Right n -> do
+ encode_node_announcement n @?= Right wire
+ pure (Right (na_addresses n, na_unknown_addresses n))
--- | Big-endian u16 (low 16 bits).
-u16be :: Int -> BS.ByteString
-u16be n = BS.pack [fromIntegral (n `div` 256), fromIntegral n]
+node_announcement_tests :: TestTree
+node_announcement_tests = testGroup "node_announcement" [
+ testCase "signed vector: fields, re-encoding, hash, verification" $ do
+ let (h, dh) = node_announcement_vector
+ p <- hex h
+ d <- hex dh
+ n <- expectRight "decode" (decode_node_announcement p)
+ fs <- hex "08000000000002aaa2"
+ nid <- hex
+ "0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c"
+ v4 <- must "ipv4" (ipv4_addr "\127\0\0\1")
+ v6 <- must "ipv6" (ipv6_addr (BS.replicate 15 0 <> "\1"))
+ t2 <- must "tor v2" (tor_v2_addr (BS.replicate 12 0xaa))
+ t3 <- must "tor v3" (tor_v3_addr (BS.replicate 35 0xbb))
+ dns <- must "hostname" (hostname "ln.example.com")
+ BOLT9.render (na_features n) @?= fs
+ na_timestamp n @?= 1700000000
+ BOLT1.un_point (na_node_id n) @?= nid
+ na_rgb_color n @?= RgbColor 0x31 0x41 0x59
+ BS.takeWhile (/= 0) (un_alias (na_alias n)) @?= "ppad-bolt7"
+ na_addresses n @?=
+ [ AddrIPv4 v4 9735, AddrIPv6 v6 9735, AddrTorV2 t2
+ , AddrTorV3 t3 9735, AddrDNS dns 9735 ]
+ na_unknown_addresses n @?= "\x06\x01\x02\x03"
+ BOLT1.un_tlv_stream (na_tlvs n) @?= [TlvRecord 101 "ext"]
+ encode_node_announcement n @?= Right p
+ node_announcement_hash n @?= Right d
+ assertBool "verifies" (verify_node_announcement n)
+ assertBool "tampered"
+ (not (verify_node_announcement n { na_timestamp = 1700000001 }))
+ usable_addresses (na_addresses n) @?=
+ [ AddrIPv4 v4 9735, AddrIPv6 v6 9735, AddrTorV3 t3 9735
+ , AddrDNS dns 9735 ]
+ validate_node_announcement n @?= Right ()
+ , testCase "keeps deprecated Tor v2 descriptors" $ do
+ fx <- fixtures
+ r <- addrs_of fx (desc_v4 <> desc_tor_v2 <> desc_v6 <> desc_tor_v3)
+ fmap (map kind . fst) r @?= Right [1, 3, 2, 4]
+ , testCase "stops at the first unknown type, keeping the rest" $ do
+ fx <- fixtures
+ let rest = BS.pack [6, 1, 2, 3] <> desc_v6
+ r <- addrs_of fx (desc_v4 <> rest)
+ fmap (\(as, u) -> (map kind as, u)) r @?= Right ([1], rest)
+ , testCase "stops at a type-0 descriptor" $ do
+ fx <- fixtures
+ let bytes = BS.singleton 0 <> desc_v4
+ r <- addrs_of fx bytes
+ fmap (\(as, u) -> (map kind as, u)) r @?= Right ([], bytes)
+ , testCase "keeps descriptors with port 0" $ do
+ fx <- fixtures
+ v4 <- must "ipv4" (ipv4_addr "\127\0\0\1")
+ r <- addrs_of fx (BS.pack [1, 127, 0, 0, 1, 0, 0])
+ r @?= Right ([AddrIPv4 v4 0], BS.empty)
+ , testCase "keeps empty and non-ASCII hostnames" $ do
+ fx <- fixtures
+ r <- addrs_of fx (desc_dns "" <> desc_dns "\xff\&ab")
+ fmap (map (\a -> case a of
+ AddrDNS h _ -> un_hostname h
+ _ -> "?") . fst) r
+ @?= Right ["", "\xff\&ab"]
+ , testCase "rejects a truncated descriptor of a known type" $ do
+ fx <- fixtures
+ let truncated bytes = decode_node_announcement (node_wire fx bytes)
+ @?= Left DecodeInsufficientBytes
+ truncated (BS.pack [1, 127, 0, 0])
+ truncated (BS.pack [2, 0, 0])
+ truncated (BS.pack [3, 1, 2, 3])
+ truncated (BS.cons 4 (BS.replicate 36 0))
+ truncated (BS.pack [5, 4, 0x61])
+ , testCase "rejects a node_id without a compressed prefix" $ do
+ fx <- fixtures
+ let wire = set_byte 70 0x04 (node_wire fx BS.empty)
+ decode_node_announcement wire @?= Left DecodeInvalidPoint
+ , testCase "na_unknown_addresses must not begin with a known type" $ do
+ fx <- fixtures
+ let with u = encode_node_announcement
+ (node_fixture fx []) { na_unknown_addresses = u }
+ mapM_ (\t -> with (BS.pack [t, 0]) @?= Left EncodeInvalidAddresses)
+ [1 .. 5]
+ assertBool "type 0" (either (const False) (const True)
+ (with (BS.pack [0, 1])))
+ assertBool "type 6" (either (const False) (const True)
+ (with (BS.pack [6, 1])))
+ , testCase "extension with an unknown even type is rejected" $ do
+ fx <- fixtures
+ decode_node_announcement (node_wire fx BS.empty <> "\x02\x00")
+ @?= Left (DecodeTlvError (TlvUnknownEvenType 2))
+ ]
+ where
+ kind :: Address -> Int
+ kind a = case a of
+ AddrIPv4 _ _ -> 1
+ AddrIPv6 _ _ -> 2
+ AddrTorV2 _ -> 3
+ AddrTorV3 _ _ -> 4
+ AddrDNS _ _ -> 5
--- | Address descriptors (port 9735) and the values they decode to.
-descV4, descV6, descTorV2 :: BS.ByteString
-descV4 = BS.pack [1, 127, 0, 0, 1, 0x26, 0x07]
-descV6 = BS.pack (2 : replicate 15 0 ++ [1, 0x26, 0x07])
-descTorV2 = BS.cons 3 (BS.replicate 12 0xaa)
+-- channel_update -------------------------------------------------------------
-descDns :: BS.ByteString -> BS.ByteString
-descDns h = BS.pack [5, fromIntegral (BS.length h)] <> h <> u16be 9735
+channel_update_tests :: TestTree
+channel_update_tests = testGroup "channel_update" [
+ testCase "encoder writes the flags and htlc_maximum_msat" $ do
+ u <- fixtures >>= update_fixture
+ let wire = encode_channel_update u
+ BS.length wire @?= 136
+ BS.indexMaybe wire 108 @?= Just 0x01
+ BS.indexMaybe wire 109 @?= Just 0x00
+ BS.drop 128 wire @?= BS.pack [0, 0, 0, 0, 0x3b, 0x9a, 0xca, 0]
+ , testCase "dont_forward" $ do
+ u <- fixtures >>= update_fixture
+ let wire = encode_channel_update
+ u { cu_message_flags = message_flags DontForward }
+ BS.indexMaybe wire 108 @?= Just 0x03
+ d <- expectRight "decode" (decode_channel_update wire)
+ message_flags_forwarding (cu_message_flags d) @?= DontForward
+ , testCase "keeps a clear must_be_one bit" $ do
+ u <- fixtures >>= update_fixture
+ let wire = set_byte 108 0x00 (encode_channel_update u)
+ d <- expectRight "decode" (decode_channel_update wire)
+ d @?= u { cu_message_flags = MessageFlags 0 }
+ encode_channel_update d @?= wire
+ , testCase "keeps unassigned flag bits" $ do
+ u <- fixtures >>= update_fixture
+ let wire = set_byte 109 0xfd (set_byte 108 0xfc (encode_channel_update u))
+ d <- expectRight "decode" (decode_channel_update wire)
+ cu_message_flags d @?= MessageFlags 0xfc
+ cu_channel_flags d @?= ChannelFlags 0xfd
+ channel_flags_direction (cu_channel_flags d) @?= NodeTwo
+ channel_flags_status (cu_channel_flags d) @?= Enabled
+ encode_channel_update d @?= wire
+ , testCase "keeps extension records" $ do
+ u <- fixtures >>= update_fixture
+ ext <- tlvs_of [TlvRecord 55555 "\0\0\0\0\xff\xff\xff\xed"]
+ let wire = encode_channel_update u { cu_tlvs = ext }
+ BS.length wire @?= 148
+ decode_channel_update wire @?= Right u { cu_tlvs = ext }
+ , testCase "rejects a missing htlc_maximum_msat" $ do
+ u <- fixtures >>= update_fixture
+ decode_channel_update (BS.take 128 (encode_channel_update u))
+ @?= Left DecodeInsufficientBytes
+ , testCase "rejects amounts above 21M BTC" $ do
+ u <- fixtures >>= update_fixture
+ let wire = encode_channel_update u
+ over = big_endian 8 0x1d24b2dfac520001
+ at off = BS.take off wire <> over <> BS.drop (off + 8) wire
+ decode_channel_update (at 112) @?= Left DecodeInvalidAmount
+ decode_channel_update (at 128) @?= Left DecodeInvalidAmount
+ max_msat <- msat 0x1d24b2dfac520000
+ fmap cu_htlc_maximum_msat (decode_channel_update
+ (BS.take 128 wire <> big_endian 8 0x1d24b2dfac520000))
+ @?= Right max_msat
+ , testCase "rejects a malformed extension" $ do
+ u <- fixtures >>= update_fixture
+ let wire = encode_channel_update u
+ decode_channel_update (wire <> "\x02\x00")
+ @?= Left (DecodeTlvError (TlvUnknownEvenType 2))
+ decode_channel_update (wire <> "\x01\x05\xaa")
+ @?= Left (DecodeTlvError TlvTruncated)
+ decode_channel_update (wire <> "\xfd\x00\x01\x00")
+ @?= Left (DecodeTlvError TlvNonMinimalBigSize)
+ , testCase "rejects truncated input" $
+ decode_channel_update (BS.replicate 50 0) @?= Left DecodeInsufficientBytes
+ ]
-addrV4, addrV6 :: IO Address
-addrV4 = fmap (`AddrIPv4` 9735)
- (expectJust "ipv4" (ipv4Addr (BS.pack [127, 0, 0, 1])))
-addrV6 = fmap (`AddrIPv6` 9735)
- (expectJust "ipv6" (ipv6Addr (BS.pack (replicate 15 0 ++ [1]))))
+-- queries --------------------------------------------------------------------
--- Channel Update Tests --------------------------------------------------------
+-- payload builders
+qsci_wire :: Fx -> BS.ByteString -> BS.ByteString -> BS.ByteString
+qsci_wire fx ids tlvs =
+ BOLT1.un_chain_hash (fx_chain fx) <> u16be (BS.length ids) <> ids <> tlvs
-channel_update_tests :: TestTree
-channel_update_tests = testGroup "ChannelUpdate" [
- testCase "encode/decode roundtrip (NodeTwo)" $ do
- let msg = ChannelUpdate
- { chanUpdateSignature = testSignature
- , chanUpdateChainHash = testChainHash
- , chanUpdateShortChanId = testShortChannelId
- , chanUpdateTimestamp = 1234567890
- , chanUpdateChanFlags = ChannelFlags
- { cfDirection = NodeTwo, cfStatus = Enabled }
- , chanUpdateCltvExpDelta = CltvExpiryDelta 144
- , chanUpdateHtlcMinMsat = HtlcMinimumMsat 1000
- , chanUpdateFeeBaseMsat = FeeBaseMsat 1000
- , chanUpdateFeeProportional = FeeProportionalMillionths 100
- , chanUpdateHtlcMaxMsat = HtlcMaximumMsat 1000000000
- }
- encoded = encodeChannelUpdate msg
- case decodeChannelUpdate encoded of
- Right (decoded, _) -> decoded @?= msg
- Left e -> assertFailure $ "decode failed: " ++ show e
- , testCase "encode/decode roundtrip (NodeOne)" $ do
- let msg = ChannelUpdate
- { chanUpdateSignature = testSignature
- , chanUpdateChainHash = testChainHash
- , chanUpdateShortChanId = testShortChannelId
- , chanUpdateTimestamp = 1234567890
- , chanUpdateChanFlags = ChannelFlags
- { cfDirection = NodeOne, cfStatus = Enabled }
- , chanUpdateCltvExpDelta = CltvExpiryDelta 40
- , chanUpdateHtlcMinMsat = HtlcMinimumMsat 1000
- , chanUpdateFeeBaseMsat = FeeBaseMsat 500
- , chanUpdateFeeProportional = FeeProportionalMillionths 50
- , chanUpdateHtlcMaxMsat = HtlcMaximumMsat 1000000000
- }
- encoded = encodeChannelUpdate msg
- case decodeChannelUpdate encoded of
- Right (decoded, _) -> decoded @?= msg
- Left e -> assertFailure $ "decode failed: " ++ show e
- , testCase "encoder sets must_be_one and emits htlc_maximum_msat" $ do
- let encoded = encodeChannelUpdate testChannelUpdate
- BS.length encoded @?= 136
- BS.take 1 (BS.drop 108 encoded) @?= BS.singleton 0x01
- BS.drop 128 encoded @?= BS.pack [0, 0, 0, 0, 0x3b, 0x9a, 0xca, 0]
- , testCase "decodes htlc_maximum_msat when must_be_one is clear" $ do
- -- receivers ignore must_be_one; the field is always present
- let wire = setByte 108 0x00 (encodeChannelUpdate testChannelUpdate)
- case decodeChannelUpdate wire of
- Right (decoded, rest) -> do
- decoded @?= testChannelUpdate
- rest @?= BS.empty
- Left e -> assertFailure $ "decode failed: " ++ show e
- , testCase "rejects update without htlc_maximum_msat" $ do
- let wire = BS.take 128 (encodeChannelUpdate testChannelUpdate)
- decodeChannelUpdate wire @?= Left DecodeInsufficientBytes
+qcr_wire :: Fx -> BS.ByteString -> BS.ByteString
+qcr_wire fx tlvs = mconcat
+ [ BOLT1.un_chain_hash (fx_chain fx)
+ , big_endian 4 600000
+ , big_endian 4 10000
+ , tlvs
]
--- Announcement Signatures Tests -----------------------------------------------
-
-announcement_signatures_tests :: TestTree
-announcement_signatures_tests = testGroup "AnnouncementSignatures" [
- testCase "encode/decode roundtrip" $ do
- let msg = AnnouncementSignatures
- { annSigChannelId = testChannelId
- , annSigShortChanId = testShortChannelId
- , annSigNodeSig = testSignature
- , annSigBitcoinSig = testSignature
- }
- encoded = encodeAnnouncementSignatures msg
- case decodeAnnouncementSignatures encoded of
- Right (decoded, _) -> decoded @?= msg
- Left e -> assertFailure $ "decode failed: " ++ show e
+rcr_wire :: Fx -> Word8 -> BS.ByteString -> BS.ByteString -> BS.ByteString
+rcr_wire fx sync ids tlvs = mconcat
+ [ BOLT1.un_chain_hash (fx_chain fx)
+ , big_endian 4 600000
+ , big_endian 4 10000
+ , BS.singleton sync
+ , u16be (BS.length ids)
+ , ids
+ , tlvs
]
--- Query Tests -----------------------------------------------------------------
+-- encoded_short_ids for some short channel ids
+encoded_ids :: [ShortChannelId] -> BS.ByteString
+encoded_ids ss = BS.cons 0 (foldMap BOLT1.encode_short_channel_id ss)
+
+-- TLV stream rules for a query decoder, given its known types and the
+-- unknown records it keeps
+tlv_stream_tests
+ :: String
+ -> BS.ByteString
+ -> (Fx -> BS.ByteString -> Either DecodeError [TlvRecord])
+ -> TestTree
+tlv_stream_tests name known dec = testGroup name [
+ testCase "empty stream" $ do
+ fx <- fixtures
+ dec fx BS.empty @?= Right []
+ , testCase "keeps unknown odd types" $ do
+ fx <- fixtures
+ dec fx (BS.pack [5, 1, 0x2a, 7, 0]) @?=
+ Right [TlvRecord 5 "\x2a", TlvRecord 7 ""]
+ , testCase "rejects unknown even types" $ do
+ fx <- fixtures
+ dec fx (BS.pack [2, 0]) @?= Left (DecodeTlvError (TlvUnknownEvenType 2))
+ , testCase "rejects a length past the end" $ do
+ fx <- fixtures
+ dec fx (BS.pack [5, 5, 0xaa]) @?= Left (DecodeTlvError TlvTruncated)
+ , testCase "rejects repeated types" $ do
+ fx <- fixtures
+ dec fx (BS.pack [5, 0, 5, 0])
+ @?= Left (DecodeTlvError TlvNotStrictlyIncreasing)
+ , testCase "rejects a non-minimal BigSize" $ do
+ fx <- fixtures
+ dec fx (BS.pack [0xfd, 0x00, 0x05, 0x00])
+ @?= Left (DecodeTlvError TlvNonMinimalBigSize)
+ , testCase "known records are typed, not kept as unknown" $ do
+ fx <- fixtures
+ dec fx known @?= Right []
+ dec fx (known <> BS.pack [5, 0]) @?= Right [TlvRecord 5 ""]
+ ]
query_tests :: TestTree
-query_tests = testGroup "Query Messages" [
- testGroup "QueryShortChannelIds" [
- testCase "encode/decode roundtrip" $ do
- let msg = QueryShortChannelIds
- { queryScidsChainHash = testChainHash
- , queryScidsData = BS.replicate 24 0xab -- 3 SCIDs
- , queryScidsTlvs = emptyTlvs
- }
- case encodeQueryShortChannelIds msg of
- Left e -> assertFailure $ "encode failed: " ++ show e
- Right encoded -> case decodeQueryShortChannelIds encoded of
- Right (decoded, _) -> do
- queryScidsChainHash decoded @?= queryScidsChainHash msg
- queryScidsData decoded @?= queryScidsData msg
- Left e -> assertFailure $ "decode failed: " ++ show e
- ]
- , testGroup "ReplyShortChannelIdsEnd" [
- testCase "encode/decode roundtrip" $ do
- let msg = ReplyShortChannelIdsEnd
- { replyScidsChainHash = testChainHash
- , replyScidsFullInfo = 1
- }
- encoded = encodeReplyShortChannelIdsEnd msg
- case decodeReplyShortChannelIdsEnd encoded of
- Right (decoded, _) -> decoded @?= msg
- Left e -> assertFailure $ "decode failed: " ++ show e
+query_tests = testGroup "queries" [
+ tlv_stream_tests "query_short_channel_ids TLVs" (BS.pack [1, 2, 0, 0])
+ $ \fx tlvs ->
+ fmap (BOLT1.un_tlv_stream . qsci_tlvs)
+ (decode_query_short_channel_ids
+ (qsci_wire fx (encoded_ids [fx_scid fx]) tlvs))
+ , tlv_stream_tests "query_channel_range TLVs" (BS.pack [1, 1, 3])
+ $ \fx tlvs ->
+ fmap (BOLT1.un_tlv_stream . qcr_tlvs)
+ (decode_query_channel_range (qcr_wire fx tlvs))
+ , tlv_stream_tests "reply_channel_range TLVs"
+ (BS.pack ([1, 9] ++ replicate 9 0 ++ [3, 8] ++ replicate 8 0))
+ $ \fx tlvs ->
+ fmap (BOLT1.un_tlv_stream . rcr_tlvs)
+ (decode_reply_channel_range
+ (rcr_wire fx 1 (encoded_ids [fx_scid fx]) tlvs))
+ , testGroup "query_short_channel_ids" [
+ testCase "query_flags" $ do
+ fx <- fixtures
+ let ss = take 2 (fx_scids fx)
+ wire = qsci_wire fx (encoded_ids ss) (BS.pack [1, 3, 0, 0x01, 0x1f])
+ m = QueryShortChannelIds (fx_chain fx) ss
+ (Just [QueryFlags 1, QueryFlags 31]) BOLT1.empty_tlv_stream
+ decode_query_short_channel_ids wire @?= Right m
+ encode_query_short_channel_ids m @?= Right wire
+ , testCase "query_flags beyond one byte" $ do
+ fx <- fixtures
+ let ss = take 1 (fx_scids fx)
+ wire = qsci_wire fx (encoded_ids ss)
+ (BS.pack [1, 4, 0, 0xfd, 0x01, 0x00])
+ fmap qsci_query_flags (decode_query_short_channel_ids wire)
+ @?= Right (Just [QueryFlags 256])
+ , testCase "rejects a query flag count mismatch" $ do
+ fx <- fixtures
+ let ss = take 2 (fx_scids fx)
+ wire = qsci_wire fx (encoded_ids ss) (BS.pack [1, 2, 0, 0x01])
+ decode_query_short_channel_ids wire @?= Left DecodeCountMismatch
+ , testCase "rejects malformed query_flags" $ do
+ fx <- fixtures
+ let with tlv = decode_query_short_channel_ids
+ (qsci_wire fx (encoded_ids [fx_scid fx]) tlv)
+ with (BS.pack [1, 0]) @?= Left (DecodeInvalidTlvValue 1)
+ with (BS.pack [1, 4, 0, 0xfd, 0x00, 0x01])
+ @?= Left (DecodeInvalidTlvValue 1)
+ with (BS.pack [1, 2, 0, 0xfd]) @?= Left (DecodeInvalidTlvValue 1)
+ with (BS.pack [1, 2, 1, 0x01]) @?= Left (DecodeUnknownEncoding 1)
+ , testCase "rejects malformed encoded_short_ids" $ do
+ fx <- fixtures
+ let with ids = decode_query_short_channel_ids (qsci_wire fx ids "")
+ with BS.empty @?= Left DecodeInvalidShortChannelIds
+ with (BS.cons 0 (BS.replicate 7 0))
+ @?= Left DecodeInvalidShortChannelIds
+ with (BS.cons 0 (BS.replicate 9 0))
+ @?= Left DecodeInvalidShortChannelIds
+ with (BS.cons 1 (BS.replicate 8 0)) @?= Left (DecodeUnknownEncoding 1)
+ with (BS.cons 2 (BS.replicate 8 0)) @?= Left (DecodeUnknownEncoding 2)
+ , testCase "an empty list still carries its encoding type" $ do
+ fx <- fixtures
+ let m = QueryShortChannelIds (fx_chain fx) [] Nothing
+ BOLT1.empty_tlv_stream
+ wire = qsci_wire fx (BS.singleton 0) ""
+ encode_query_short_channel_ids m @?= Right wire
+ decode_query_short_channel_ids wire @?= Right m
]
- , testGroup "QueryChannelRange" [
- testCase "encode/decode roundtrip" $ do
- let msg = QueryChannelRange
- { queryRangeChainHash = testChainHash
- , queryRangeFirstBlock = BlockHeight 600000
- , queryRangeNumBlocks = BlockCount 10000
- , queryRangeTlvs = emptyTlvs
- }
- encoded = encodeQueryChannelRange msg
- case decodeQueryChannelRange encoded of
- Right (decoded, _) -> do
- queryRangeChainHash decoded @?= queryRangeChainHash msg
- queryRangeFirstBlock decoded @?= queryRangeFirstBlock msg
- queryRangeNumBlocks decoded @?= queryRangeNumBlocks msg
- Left e -> assertFailure $ "decode failed: " ++ show e
- ]
- , testGroup "ReplyChannelRange" [
- testCase "encode/decode roundtrip" $ do
- let msg = ReplyChannelRange
- { replyRangeChainHash = testChainHash
- , replyRangeFirstBlock = BlockHeight 600000
- , replyRangeNumBlocks = BlockCount 10000
- , replyRangeSyncComplete = 1
- , replyRangeData = BS.replicate 16 0xcd
- , replyRangeTlvs = emptyTlvs
- }
- case encodeReplyChannelRange msg of
- Left e -> assertFailure $ "encode failed: " ++ show e
- Right encoded -> case decodeReplyChannelRange encoded of
- Right (decoded, _) -> do
- replyRangeChainHash decoded @?= replyRangeChainHash msg
- replyRangeFirstBlock decoded @?= replyRangeFirstBlock msg
- replyRangeNumBlocks decoded @?= replyRangeNumBlocks msg
- replyRangeSyncComplete decoded @?= replyRangeSyncComplete msg
- replyRangeData decoded @?= replyRangeData msg
- Left e -> assertFailure $ "decode failed: " ++ show e
+ , testGroup "query_channel_range" [
+ testCase "query_option" $ do
+ fx <- fixtures
+ let wire = qcr_wire fx (BS.pack [1, 3, 0xfd, 0x01, 0x00])
+ fmap qcr_query_option (decode_query_channel_range wire)
+ @?= Right (Just (QueryOption 256))
+ , testCase "rejects malformed query_option" $ do
+ fx <- fixtures
+ let with tlv = decode_query_channel_range (qcr_wire fx tlv)
+ with (BS.pack [1, 0]) @?= Left (DecodeInvalidTlvValue 1)
+ with (BS.pack [1, 2, 3, 0]) @?= Left (DecodeInvalidTlvValue 1)
+ with (BS.pack [1, 3, 0xfd, 0x00, 0x03])
+ @?= Left (DecodeInvalidTlvValue 1)
+ , testCase "rejects truncated input" $ do
+ fx <- fixtures
+ decode_query_channel_range (BS.take 39 (qcr_wire fx ""))
+ @?= Left DecodeInsufficientBytes
]
- , testGroup "GossipTimestampFilter" [
- testCase "encode/decode roundtrip" $ do
- let msg = GossipTimestampFilter
- { gossipFilterChainHash = testChainHash
- , gossipFilterFirstTimestamp = 1609459200
- , gossipFilterTimestampRange = 86400
- }
- encoded = encodeGossipTimestampFilter msg
- case decodeGossipTimestampFilter encoded of
- Right (decoded, _) -> decoded @?= msg
- Left e -> assertFailure $ "decode failed: " ++ show e
+ , testGroup "reply_channel_range" [
+ testCase "rejects malformed timestamps" $ do
+ fx <- fixtures
+ let ss = take 2 (fx_scids fx)
+ with tlv = decode_reply_channel_range
+ (rcr_wire fx 1 (encoded_ids ss) tlv)
+ with (BS.pack [1, 0]) @?= Left (DecodeInvalidTlvValue 1)
+ with (BS.pack [1, 9, 1] <> BS.replicate 8 0)
+ @?= Left (DecodeUnknownEncoding 1)
+ with (BS.pack [1, 9, 0] <> BS.replicate 8 0)
+ @?= Left DecodeCountMismatch
+ with (BS.pack [1, 13, 0] <> BS.replicate 12 0)
+ @?= Left (DecodeInvalidTlvValue 1)
+ , testCase "rejects malformed checksums" $ do
+ fx <- fixtures
+ let ss = take 2 (fx_scids fx)
+ with tlv = decode_reply_channel_range
+ (rcr_wire fx 1 (encoded_ids ss) tlv)
+ with (BS.pack [3, 8] <> BS.replicate 8 0) @?= Left DecodeCountMismatch
+ with (BS.pack [3, 12] <> BS.replicate 12 0)
+ @?= Left (DecodeInvalidTlvValue 3)
+ fmap rcr_checksums (with (BS.pack [3, 16] <> BS.replicate 16 0))
+ @?= Right (Just (replicate 2 (ChannelUpdateChecksums 0 0)))
+ , testCase "rejects a sync_complete other than 0 or 1" $ do
+ fx <- fixtures
+ let with s = fmap rcr_sync_complete (decode_reply_channel_range
+ (rcr_wire fx s (encoded_ids []) ""))
+ with 0 @?= Right False
+ with 1 @?= Right True
+ with 2 @?= Left DecodeInvalidBool
+ , testCase "rejects malformed encoded_short_ids" $ do
+ fx <- fixtures
+ let with ids = decode_reply_channel_range (rcr_wire fx 1 ids "")
+ with BS.empty @?= Left DecodeInvalidShortChannelIds
+ with (BS.cons 0 (BS.replicate 12 0))
+ @?= Left DecodeInvalidShortChannelIds
+ with (BS.cons 1 (BS.replicate 8 0)) @?= Left (DecodeUnknownEncoding 1)
]
- , tlvStreamTests "query_short_channel_ids TLVs" [1] $ \tlvs ->
- fmap (unTlvStream . queryScidsTlvs . fst)
- (decodeQueryShortChannelIds (mconcat
- [ unChainHash testChainHash
- , u16be 9
- , encodeShortChannelIdList [testShortChannelId]
- , tlvs
- ]))
- , tlvStreamTests "query_channel_range TLVs" [1] $ \tlvs ->
- fmap (unTlvStream . queryRangeTlvs . fst)
- (decodeQueryChannelRange (mconcat
- [ unChainHash testChainHash
- , BS.pack [0, 9, 0x27, 0xc0] -- first_blocknum 600000
- , BS.pack [0, 0, 0x27, 0x10] -- number_of_blocks 10000
- , tlvs
- ]))
- , tlvStreamTests "reply_channel_range TLVs" [1, 3] $ \tlvs ->
- fmap (unTlvStream . replyRangeTlvs . fst)
- (decodeReplyChannelRange (mconcat
- [ unChainHash testChainHash
- , BS.pack [0, 9, 0x27, 0xc0] -- first_blocknum 600000
- , BS.pack [0, 0, 0x27, 0x10] -- number_of_blocks 10000
- , BS.singleton 1 -- sync_complete
- , u16be 9
- , encodeShortChannelIdList [testShortChannelId]
- , tlvs
- ]))
]
--- | TLV stream tests for a query decoder, given its known TLV types
--- and a function decoding a message that ends in the given TLV bytes.
-tlvStreamTests
- :: String
- -> [Word8]
- -> (BS.ByteString -> Either DecodeError [TlvRecord])
- -> TestTree
-tlvStreamTests name known dec = testGroup name [
- testCase "empty stream" $
- dec BS.empty @?= Right []
- , testCase "keeps known types" $
- dec (BS.pack (concatMap (\t -> [t, 1, 0x2a]) known))
- @?= Right [TlvRecord (fromIntegral t) "\x2a" | t <- known]
- , testCase "skips unknown odd types" $
- dec (BS.pack [5, 0]) @?= Right []
- , testCase "rejects unknown even types" $
- dec (BS.pack [2, 0]) @?= Left DecodeTlvError
- , testCase "rejects a length past the end" $
- dec (BS.pack [1, 5, 0xaa]) @?= Left DecodeTlvError
- , testCase "rejects repeated types" $
- dec (BS.pack [1, 0, 1, 0]) @?= Left DecodeTlvError
- , testCase "rejects a non-minimal BigSize" $
- dec (BS.pack [0xfd, 0x00, 0x01, 0x00]) @?= Left DecodeTlvError
+-- other messages -------------------------------------------------------------
+
+other_message_tests :: TestTree
+other_message_tests = testGroup "other messages" [
+ testCase "reply_short_channel_ids_end" $ do
+ fx <- fixtures
+ let ch = BOLT1.un_chain_hash (fx_chain fx)
+ ext <- tlvs_of [TlvRecord 9 "x"]
+ decode_reply_short_channel_ids_end (ch <> "\x01")
+ @?= Right (ReplyShortChannelIdsEnd (fx_chain fx) True
+ BOLT1.empty_tlv_stream)
+ decode_reply_short_channel_ids_end (ch <> "\x00\x09\x01x")
+ @?= Right (ReplyShortChannelIdsEnd (fx_chain fx) False ext)
+ decode_reply_short_channel_ids_end (ch <> "\x02")
+ @?= Left DecodeInvalidBool
+ decode_reply_short_channel_ids_end ch @?= Left DecodeInsufficientBytes
+ , testCase "announcement_signatures" $ do
+ fx <- fixtures
+ ext <- tlvs_of [TlvRecord 1 "a"]
+ let m = AnnouncementSignatures (fx_cid fx) (fx_scid fx) (fx_sig fx)
+ (fx_sig fx) ext
+ wire = encode_announcement_signatures m
+ BS.length wire @?= 32 + 8 + 64 + 64 + 3
+ decode_announcement_signatures wire @?= Right m
+ decode_announcement_signatures (BS.take 167 wire)
+ @?= Left DecodeInsufficientBytes
+ -- type 1 is odd, so it may appear in an extension; 2 may not
+ decode_announcement_signatures (BS.take 168 wire <> "\x02\x00")
+ @?= Left (DecodeTlvError (TlvUnknownEvenType 2))
+ , testCase "gossip_timestamp_filter" $ do
+ fx <- fixtures
+ let m = GossipTimestampFilter (fx_chain fx) 1609459200 86400
+ BOLT1.empty_tlv_stream
+ wire = encode_gossip_timestamp_filter m
+ BS.drop 32 wire @?= BS.pack [0x5f, 0xee, 0x66, 0x00, 0, 1, 0x51, 0x80]
+ decode_gossip_timestamp_filter wire @?= Right m
+ decode_gossip_timestamp_filter (BS.take 39 wire)
+ @?= Left DecodeInsufficientBytes
+ , testCase "channel_announcement rejects bad points" $ do
+ fx <- fixtures
+ wire <- expectRight "encode"
+ (encode_channel_announcement (announcement_fixture fx))
+ -- node_id_1 starts after the signatures, features, chain hash
+ -- and short channel id
+ decode_channel_announcement (set_byte 298 0x04 wire)
+ @?= Left DecodeInvalidPoint
+ decode_channel_announcement (BS.take 429 wire)
+ @?= Left DecodeInsufficientBytes
]
--- SCID List Tests ------------------------------------------------------------
-
-scid_list_tests :: TestTree
-scid_list_tests = testGroup "SCID List Encoding" [
- testCase "encode/decode roundtrip empty list" $ do
- let encoded = encodeShortChannelIdList []
- case decodeShortChannelIdList encoded of
- Right decoded -> decoded @?= []
- Left e -> assertFailure $ "decode failed: " ++ show e
- , testCase "encode/decode roundtrip single SCID" $ do
- let scids = [mkScid 539268 845 1]
- encoded = encodeShortChannelIdList scids
- case decodeShortChannelIdList encoded of
- Right decoded -> decoded @?= scids
- Left e -> assertFailure $ "decode failed: " ++ show e
- , testCase "encode/decode roundtrip multiple SCIDs" $ do
- let scids = [ mkScid 100000 1 0
- , mkScid 200000 2 1
- , mkScid 300000 3 2
- ]
- encoded = encodeShortChannelIdList scids
- case decodeShortChannelIdList encoded of
- Right decoded -> decoded @?= scids
- Left e -> assertFailure $ "decode failed: " ++ show e
- , testCase "encoding has correct format" $ do
- let scids = [mkScid 1 2 3]
- encoded = encodeShortChannelIdList scids
- -- First byte should be 0 (encoding type)
- BS.index encoded 0 @?= 0
- -- Total length: 1 (type) + 8 (SCID) = 9
- BS.length encoded @?= 9
- , testCase "decode rejects unknown encoding type" $ do
- -- Encoding type 1 (zlib compressed) is not supported
- let badEncoded = BS.cons 1 (scidToBytes testShortChannelId)
- case decodeShortChannelIdList badEncoded of
- Left _ -> pure ()
- Right _ -> assertFailure "should reject encoding type 1"
+-- encoding errors ------------------------------------------------------------
+
+encode_error_tests :: TestTree
+encode_error_tests = testGroup "encoding errors" [
+ testCase "channel_announcement features" $ do
+ fx <- fixtures
+ let feats n = (announcement_fixture fx)
+ { ca_features = BOLT9.parse (BS.replicate n 1) }
+ encode_channel_announcement (feats 65536) @?= Left EncodeLengthOverflow
+ wire <- expectRight "encode" (encode_channel_announcement (feats 65535))
+ decode_channel_announcement wire @?= Right (feats 65535)
+ , testCase "node_announcement features and addresses" $ do
+ fx <- fixtures
+ v4 <- must "ipv4" (ipv4_addr "\127\0\0\1")
+ let base = node_fixture fx []
+ encode_node_announcement base
+ { na_features = BOLT9.parse (BS.replicate 65536 1) }
+ @?= Left EncodeLengthOverflow
+ -- 10923 IPv4 descriptors take 65538 bytes
+ let v4s n = node_fixture fx (replicate n (AddrIPv4 v4 1))
+ encode_node_announcement (v4s 10923) @?= Left EncodeLengthOverflow
+ wire <- expectRight "encode" (encode_node_announcement (v4s 9362))
+ fmap (length . na_addresses) (decode_node_announcement wire)
+ @?= Right 9362
+ , testCase "short channel id lists" $ do
+ fx <- fixtures
+ let ids n = map ShortChannelId [1 .. n]
+ q n = QueryShortChannelIds (fx_chain fx) (ids n) Nothing
+ BOLT1.empty_tlv_stream
+ r n = ReplyChannelRange (fx_chain fx) 0 1 True (ids n) Nothing
+ Nothing BOLT1.empty_tlv_stream
+ encode_query_short_channel_ids (q 8192) @?= Left EncodeLengthOverflow
+ encode_reply_channel_range (r 8192) @?= Left EncodeLengthOverflow
+ wire <- expectRight "encode" (encode_query_short_channel_ids (q 8191))
+ decode_query_short_channel_ids wire @?= Right (q 8191)
+ , testCase "count mismatches" $ do
+ fx <- fixtures
+ let ss = fx_scids fx
+ q = QueryShortChannelIds (fx_chain fx) ss (Just [QueryFlags 1])
+ BOLT1.empty_tlv_stream
+ r = ReplyChannelRange (fx_chain fx) 0 1 True ss Nothing Nothing
+ BOLT1.empty_tlv_stream
+ encode_query_short_channel_ids q @?= Left EncodeCountMismatch
+ encode_reply_channel_range r
+ { rcr_timestamps = Just [ChannelUpdateTimestamps 1 2] }
+ @?= Left EncodeCountMismatch
+ encode_reply_channel_range r
+ { rcr_checksums = Just (replicate 4 (ChannelUpdateChecksums 1 2)) }
+ @?= Left EncodeCountMismatch
+ , testCase "extra records must not use known types" $ do
+ fx <- fixtures
+ t1 <- tlvs_of [TlvRecord 1 "\0"]
+ t3 <- tlvs_of [TlvRecord 3 ""]
+ t5 <- tlvs_of [TlvRecord 5 ""]
+ let q = QueryShortChannelIds (fx_chain fx) [] Nothing
+ c = QueryChannelRange (fx_chain fx) 0 1 Nothing
+ r = ReplyChannelRange (fx_chain fx) 0 1 True [] Nothing Nothing
+ encode_query_short_channel_ids (q t1) @?= Left EncodeInvalidTlvs
+ encode_query_channel_range (c t1) @?= Left EncodeInvalidTlvs
+ encode_reply_channel_range (r t1) @?= Left EncodeInvalidTlvs
+ encode_reply_channel_range (r t3) @?= Left EncodeInvalidTlvs
+ wire <- expectRight "encode" (encode_reply_channel_range (r t5))
+ decode_reply_channel_range wire @?= Right (r t5)
]
--- Hash Tests -----------------------------------------------------------------
-
-hash_tests :: TestTree
-hash_tests = testGroup "Hash Functions" [
- testGroup "CRC32C" [
- testCase "known test vector '123456789'" $ do
- -- Standard CRC-32C test vector
- crc32c "123456789" @?= 0xe3069283
- , testCase "empty string" $ do
- crc32c "" @?= 0x00000000
- ]
- , testGroup "Signature Hashes" [
- testCase "channelAnnouncementHash produces 32 bytes" $ do
- -- Create a minimal valid encoded message
- case encodeChannelAnnouncement ChannelAnnouncement
- { channelAnnNodeSig1 = testSignature
- , channelAnnNodeSig2 = testSignature
- , channelAnnBitcoinSig1 = testSignature
- , channelAnnBitcoinSig2 = testSignature
- , channelAnnFeatures = emptyFeatures
- , channelAnnChainHash = testChainHash
- , channelAnnShortChanId = testShortChannelId
- , channelAnnNodeId1 = testNodeId
- , channelAnnNodeId2 = testNodeId2
- , channelAnnBitcoinKey1 = testPoint
- , channelAnnBitcoinKey2 = testPoint
- } of
- Left e -> assertFailure $ "encode failed: " ++ show e
- Right msg -> do
- let hashVal = channelAnnouncementHash msg
- fmap BS.length hashVal @?= Just 32
- , testCase "nodeAnnouncementHash produces 32 bytes" $ do
- case encodeNodeAnnouncement NodeAnnouncement
- { nodeAnnSignature = testSignature
- , nodeAnnFeatures = emptyFeatures
- , nodeAnnTimestamp = 1234567890
- , nodeAnnNodeId = testNodeId
- , nodeAnnRgbColor = testRgbColor
- , nodeAnnAlias = testAlias
- , nodeAnnAddresses = []
- } of
- Left e -> assertFailure $ "encode failed: " ++ show e
- Right msg -> do
- let hashVal = nodeAnnouncementHash msg
- fmap BS.length hashVal @?= Just 32
- , testCase "channelUpdateHash produces 32 bytes" $ do
- let msg = encodeChannelUpdate ChannelUpdate
- { chanUpdateSignature = testSignature
- , chanUpdateChainHash = testChainHash
- , chanUpdateShortChanId = testShortChannelId
- , chanUpdateTimestamp = 1234567890
- , chanUpdateChanFlags = ChannelFlags
- { cfDirection = NodeOne
- , cfStatus = Enabled }
- , chanUpdateCltvExpDelta = CltvExpiryDelta 144
- , chanUpdateHtlcMinMsat = HtlcMinimumMsat 1000
- , chanUpdateFeeBaseMsat = FeeBaseMsat 1000
- , chanUpdateFeeProportional = FeeProportionalMillionths 100
- , chanUpdateHtlcMaxMsat = HtlcMaximumMsat 1000000000
- }
- hashVal = channelUpdateHash msg
- fmap BS.length hashVal @?= Just 32
- ]
- , testGroup "Checksum" [
- testCase "channelUpdateChecksum produces consistent result" $ do
- -- The checksum should be deterministic
- let msg = encodeChannelUpdate ChannelUpdate
- { chanUpdateSignature = testSignature
- , chanUpdateChainHash = testChainHash
- , chanUpdateShortChanId = testShortChannelId
- , chanUpdateTimestamp = 1234567890
- , chanUpdateChanFlags = ChannelFlags
- { cfDirection = NodeOne
- , cfStatus = Enabled }
- , chanUpdateCltvExpDelta = CltvExpiryDelta 144
- , chanUpdateHtlcMinMsat = HtlcMinimumMsat 1000
- , chanUpdateFeeBaseMsat = FeeBaseMsat 1000
- , chanUpdateFeeProportional = FeeProportionalMillionths 100
- , chanUpdateHtlcMaxMsat = HtlcMaximumMsat 1000000000
- }
- cs1 = channelUpdateChecksum msg
- cs2 = channelUpdateChecksum msg
- cs1 @?= cs2
- , testCase "different timestamps produce same checksum" $ do
- -- Checksum excludes timestamp field
- let msg1 = encodeChannelUpdate ChannelUpdate
- { chanUpdateSignature = testSignature
- , chanUpdateChainHash = testChainHash
- , chanUpdateShortChanId = testShortChannelId
- , chanUpdateTimestamp = 1000000000
- , chanUpdateChanFlags = ChannelFlags
- { cfDirection = NodeOne
- , cfStatus = Enabled }
- , chanUpdateCltvExpDelta = CltvExpiryDelta 144
- , chanUpdateHtlcMinMsat = HtlcMinimumMsat 1000
- , chanUpdateFeeBaseMsat = FeeBaseMsat 1000
- , chanUpdateFeeProportional = FeeProportionalMillionths 100
- , chanUpdateHtlcMaxMsat = HtlcMaximumMsat 1000000000
- }
- msg2 = encodeChannelUpdate ChannelUpdate
- { chanUpdateSignature = testSignature
- , chanUpdateChainHash = testChainHash
- , chanUpdateShortChanId = testShortChannelId
- , chanUpdateTimestamp = 2000000000
- , chanUpdateChanFlags = ChannelFlags
- { cfDirection = NodeOne
- , cfStatus = Enabled }
- , chanUpdateCltvExpDelta = CltvExpiryDelta 144
- , chanUpdateHtlcMinMsat = HtlcMinimumMsat 1000
- , chanUpdateFeeBaseMsat = FeeBaseMsat 1000
- , chanUpdateFeeProportional = FeeProportionalMillionths 100
- , chanUpdateHtlcMaxMsat = HtlcMaximumMsat 1000000000
- }
- channelUpdateChecksum msg1 @?= channelUpdateChecksum msg2
- ]
- , testGroup "Input length" [
- testCase "channel_update needs 136 bytes" $ do
- let wire = encodeChannelUpdate testChannelUpdate
- channelUpdateHash (BS.take 135 wire) @?= Nothing
- channelUpdateChecksum (BS.take 135 wire) @?= Nothing
- channelUpdateChecksum "short" @?= Nothing
- fmap BS.length (channelUpdateHash wire) @?= Just 32
- assertBool "checksum" (channelUpdateChecksum wire /= Nothing)
- , testCase "channel_announcement needs its declared features" $
- case encodeChannelAnnouncement
- (testChannelAnn (featureBits (BS.pack [1, 2]))) of
- Left e -> assertFailure $ "encode failed: " ++ show e
- Right wire -> do
- fmap BS.length (channelAnnouncementHash wire) @?= Just 32
- channelAnnouncementHash (BS.take (BS.length wire - 1) wire)
- @?= Nothing
- -- claim one more feature byte than present
- channelAnnouncementHash (setByte 257 3 wire) @?= Nothing
- channelAnnouncementHash (BS.take 257 wire) @?= Nothing
- , testCase "node_announcement needs its declared addresses" $ do
- v4 <- addrV4
- case encodeNodeAnnouncement (testNodeAnn [v4]) of
- Left e -> assertFailure $ "encode failed: " ++ show e
- Right wire -> do
- fmap BS.length (nodeAnnouncementHash wire) @?= Just 32
- nodeAnnouncementHash (BS.take (BS.length wire - 1) wire)
- @?= Nothing
- -- claim one more address byte than present (addrlen 8)
- nodeAnnouncementHash (setByte 139 8 wire) @?= Nothing
- nodeAnnouncementHash (BS.take 139 wire) @?= Nothing
- ]
+-- dispatch -------------------------------------------------------------------
+
+dispatch_tests :: TestTree
+dispatch_tests = testGroup "dispatch" [
+ testCase "unknown message types" $
+ mapM_ (\t -> decode_message t "" @?= Left (DecodeUnknownType t))
+ [0, 16, 255, 260, 266, 32768]
+ , testCase "message types" $ do
+ fx <- fixtures
+ u <- update_fixture fx
+ let e = BOLT1.empty_tlv_stream
+ ms = [ MsgChannelAnnouncement (announcement_fixture fx)
+ , MsgNodeAnnouncement (node_fixture fx [])
+ , MsgChannelUpdate u
+ , MsgAnnouncementSignatures (AnnouncementSignatures
+ (fx_cid fx) (fx_scid fx) (fx_sig fx) (fx_sig fx) e)
+ , MsgQueryShortChannelIds
+ (QueryShortChannelIds (fx_chain fx) [] Nothing e)
+ , MsgReplyShortChannelIdsEnd
+ (ReplyShortChannelIdsEnd (fx_chain fx) True e)
+ , MsgQueryChannelRange
+ (QueryChannelRange (fx_chain fx) 0 1 Nothing e)
+ , MsgReplyChannelRange (ReplyChannelRange
+ (fx_chain fx) 0 1 True [] Nothing Nothing e)
+ , MsgGossipTimestampFilter
+ (GossipTimestampFilter (fx_chain fx) 0 1 e)
+ ]
+ map message_type ms @?= [256, 257, 258, 259, 261, 262, 263, 264, 265]
+ mapM_ (\m -> do
+ p <- expectRight "encode" (encode_message m)
+ decode_message (message_type m) p @?= Right m)
+ ms
]
--- Validation Tests -----------------------------------------------------------
+-- validation -----------------------------------------------------------------
validation_tests :: TestTree
-validation_tests = testGroup "Validation" [
- testGroup "ChannelAnnouncement" [
- testCase "valid announcement passes" $ do
- let msg = ChannelAnnouncement
- { channelAnnNodeSig1 = testSignature
- , channelAnnNodeSig2 = testSignature
- , channelAnnBitcoinSig1 = testSignature
- , channelAnnBitcoinSig2 = testSignature
- , channelAnnFeatures = emptyFeatures
- , channelAnnChainHash = testChainHash
- , channelAnnShortChanId = testShortChannelId
- , channelAnnNodeId1 = testNodeId2 -- 0x02... < 0x03...
- , channelAnnNodeId2 = testNodeId -- 0x03...
- , channelAnnBitcoinKey1 = testPoint
- , channelAnnBitcoinKey2 = testPoint
- }
- validateChannelAnnouncement msg @?= Right ()
- , testCase "rejects wrong node_id order" $ do
- let msg = ChannelAnnouncement
- { channelAnnNodeSig1 = testSignature
- , channelAnnNodeSig2 = testSignature
- , channelAnnBitcoinSig1 = testSignature
- , channelAnnBitcoinSig2 = testSignature
- , channelAnnFeatures = emptyFeatures
- , channelAnnChainHash = testChainHash
- , channelAnnShortChanId = testShortChannelId
- , channelAnnNodeId1 = testNodeId -- 0x03... > 0x02...
- , channelAnnNodeId2 = testNodeId2 -- 0x02...
- , channelAnnBitcoinKey1 = testPoint
- , channelAnnBitcoinKey2 = testPoint
- }
- validateChannelAnnouncement msg @?= Left ValidateNodeIdOrdering
- ]
- , testGroup "NodeAnnouncement" [
- testCase "one DNS hostname passes" $ do
- h <- expectJust "hostname" (hostname "ln.example.com")
- v4 <- addrV4
- validateNodeAnnouncement (testNodeAnn [v4, AddrDNS h 9735])
- @?= Right ()
- , testCase "rejects more than one DNS hostname" $ do
- h1 <- expectJust "hostname" (hostname "a.example.com")
- h2 <- expectJust "hostname" (hostname "b.example.com")
- validateNodeAnnouncement
- (testNodeAnn [AddrDNS h1 9735, AddrDNS h2 9735])
- @?= Left ValidateMultipleDns
- ]
- , testGroup "ChannelUpdate" [
- testCase "valid update passes" $ do
- let msg = ChannelUpdate
- { chanUpdateSignature = testSignature
- , chanUpdateChainHash = testChainHash
- , chanUpdateShortChanId = testShortChannelId
- , chanUpdateTimestamp = 1234567890
- , chanUpdateChanFlags = ChannelFlags
- { cfDirection = NodeOne
- , cfStatus = Enabled }
- , chanUpdateCltvExpDelta = CltvExpiryDelta 144
- , chanUpdateHtlcMinMsat = HtlcMinimumMsat 1000
- , chanUpdateFeeBaseMsat = FeeBaseMsat 1000
- , chanUpdateFeeProportional = FeeProportionalMillionths 100
- , chanUpdateHtlcMaxMsat = HtlcMaximumMsat 1000000000
- }
- validateChannelUpdate msg @?= Right ()
- , testCase "rejects htlc_min > htlc_max" $ do
- let msg = ChannelUpdate
- { chanUpdateSignature = testSignature
- , chanUpdateChainHash = testChainHash
- , chanUpdateShortChanId = testShortChannelId
- , chanUpdateTimestamp = 1234567890
- , chanUpdateChanFlags = ChannelFlags
- { cfDirection = NodeOne
- , cfStatus = Enabled }
- , chanUpdateCltvExpDelta = CltvExpiryDelta 144
- , chanUpdateHtlcMinMsat = HtlcMinimumMsat 2000000000 -- > htlcMax
- , chanUpdateFeeBaseMsat = FeeBaseMsat 1000
- , chanUpdateFeeProportional = FeeProportionalMillionths 100
- , chanUpdateHtlcMaxMsat = HtlcMaximumMsat 1000000000
- }
- validateChannelUpdate msg @?= Left ValidateHtlcAmounts
- ]
- , testGroup "QueryChannelRange" [
- testCase "valid range passes" $ do
- let msg = QueryChannelRange
- { queryRangeChainHash = testChainHash
- , queryRangeFirstBlock = BlockHeight 600000
- , queryRangeNumBlocks = BlockCount 10000
- , queryRangeTlvs = emptyTlvs
- }
- validateQueryChannelRange msg @?= Right ()
- , testCase "rejects overflow" $ do
- let msg = QueryChannelRange
- { queryRangeChainHash = testChainHash
- , queryRangeFirstBlock = BlockHeight maxBound
- , queryRangeNumBlocks = BlockCount 10
- , queryRangeTlvs = emptyTlvs
- }
- validateQueryChannelRange msg @?= Left ValidateBlockOverflow
- ]
- , testGroup "ReplyChannelRange" [
- testCase "ascending short_channel_ids pass" $
- validateReplyChannelRange (testReplyRange
- (encodeShortChannelIdList [mkScid 1 0 0, mkScid 2 0 0]))
- @?= Right ()
- , testCase "rejects descending short_channel_ids" $
- validateReplyChannelRange (testReplyRange
- (encodeShortChannelIdList [mkScid 2 0 0, mkScid 1 0 0]))
- @?= Left ValidateScidNotAscending
- , testCase "rejects zlib encoding (type 1)" $
- validateReplyChannelRange (testReplyRange (BS.cons 1
- (scidToBytes (mkScid 2 0 0) <> scidToBytes (mkScid 1 0 0))))
- @?= Left ValidateScidEncoding
- , testCase "rejects an unknown encoding type" $
- validateReplyChannelRange (testReplyRange (BS.cons 2
- (scidToBytes (mkScid 1 0 0))))
- @?= Left ValidateScidEncoding
- , testCase "rejects a partial short_channel_id" $
- validateReplyChannelRange (testReplyRange (BS.cons 0
- (scidToBytes (mkScid 1 0 0) <> BS.replicate 7 0)))
- @?= Left ValidateScidEncoding
- ]
- ]
-
--- | A reply_channel_range fixture with the given encoded_short_ids.
-testReplyRange :: BS.ByteString -> ReplyChannelRange
-testReplyRange scids = ReplyChannelRange
- { replyRangeChainHash = testChainHash
- , replyRangeFirstBlock = BlockHeight 0
- , replyRangeNumBlocks = BlockCount 10
- , replyRangeSyncComplete = 1
- , replyRangeData = scids
- , replyRangeTlvs = emptyTlvs
- }
-
--- Error Tests -----------------------------------------------------------------
-
-error_tests :: TestTree
-error_tests = testGroup "Error Conditions" [
- testGroup "Insufficient Bytes" [
- testCase "decodeChannelAnnouncement empty" $ do
- case decodeChannelAnnouncement BS.empty of
- Left DecodeInsufficientBytes -> pure ()
- other -> assertFailure $ "expected insufficient: " ++ show other
- , testCase "decodeChannelUpdate too short" $ do
- case decodeChannelUpdate (BS.replicate 50 0x00) of
- Left DecodeInsufficientBytes -> pure ()
- other -> assertFailure $ "expected insufficient: " ++ show other
- , testCase "decodeAnnouncementSignatures too short" $ do
- case decodeAnnouncementSignatures (BS.replicate 50 0x00) of
- Left DecodeInsufficientBytes -> pure ()
- other -> assertFailure $ "expected insufficient: " ++ show other
- , testCase "decodeGossipTimestampFilter too short" $ do
- case decodeGossipTimestampFilter (BS.replicate 30 0x00) of
- Left DecodeInsufficientBytes -> pure ()
- other -> assertFailure $ "expected insufficient: " ++ show other
- ]
- , testGroup "Length overflow" [
- testCase "channel_announcement features" $ do
- let feats n = featureBits (BS.replicate n 0x01)
- encodeChannelAnnouncement (testChannelAnn (feats 65536))
- @?= Left EncodeLengthOverflow
- case encodeChannelAnnouncement (testChannelAnn (feats 65535)) of
- Left e -> assertFailure $ "encode failed: " ++ show e
- Right wire -> fmap fst (decodeChannelAnnouncement wire)
- @?= Right (testChannelAnn (feats 65535))
- , testCase "node_announcement features" $ do
- let msg = (testNodeAnn [])
- { nodeAnnFeatures = featureBits (BS.replicate 65536 0x01) }
- encodeNodeAnnouncement msg @?= Left EncodeLengthOverflow
- , testCase "node_announcement addresses" $ do
- v4 <- addrV4
- -- 10923 IPv4 descriptors take 65538 bytes
- encodeNodeAnnouncement (testNodeAnn (replicate 10923 v4))
- @?= Left EncodeLengthOverflow
- case encodeNodeAnnouncement (testNodeAnn (replicate 9362 v4)) of
- Left e -> assertFailure $ "encode failed: " ++ show e
- Right wire -> fmap (length . nodeAnnAddresses . fst)
- (decodeNodeAnnouncement wire) @?= Right 9362
- , testCase "query_short_channel_ids encoded_short_ids" $ do
- let msg = QueryShortChannelIds
- { queryScidsChainHash = testChainHash
- , queryScidsData = BS.replicate 65536 0x00
- , queryScidsTlvs = emptyTlvs
- }
- encodeQueryShortChannelIds msg @?= Left EncodeLengthOverflow
- , testCase "reply_channel_range encoded_short_ids" $ do
- let msg = ReplyChannelRange
- { replyRangeChainHash = testChainHash
- , replyRangeFirstBlock = BlockHeight 600000
- , replyRangeNumBlocks = BlockCount 10000
- , replyRangeSyncComplete = 1
- , replyRangeData = BS.replicate 65536 0x00
- , replyRangeTlvs = emptyTlvs
- }
- encodeReplyChannelRange msg @?= Left EncodeLengthOverflow
- ]
+validation_tests = testGroup "validation" [
+ testCase "channel_announcement node id order" $ do
+ fx <- fixtures
+ let a = announcement_fixture fx
+ validate_channel_announcement a @?= Right ()
+ validate_channel_announcement
+ a { ca_node_id_1 = fx_node_2 fx, ca_node_id_2 = fx_node_1 fx }
+ @?= Left ValidateNodeIdOrdering
+ validate_channel_announcement a { ca_node_id_2 = fx_node_1 fx }
+ @?= Left ValidateNodeIdOrdering
+ , testCase "node_announcement DNS hostnames" $ do
+ fx <- fixtures
+ v4 <- must "ipv4" (ipv4_addr "\127\0\0\1")
+ h1 <- must "hostname" (hostname "a.example.com")
+ h2 <- must "hostname" (hostname "b.example.com")
+ validate_node_announcement (node_fixture fx [AddrIPv4 v4 1, AddrDNS h1 1])
+ @?= Right ()
+ validate_node_announcement (node_fixture fx [AddrDNS h1 1, AddrDNS h2 1])
+ @?= Left ValidateMultipleDns
+ , testCase "channel_update htlc amounts" $ do
+ fx <- fixtures
+ u <- update_fixture fx
+ big <- msat 2000000000
+ validate_channel_update u @?= Right ()
+ validate_channel_update
+ u { cu_htlc_minimum_msat = cu_htlc_maximum_msat u } @?= Right ()
+ validate_channel_update u { cu_htlc_minimum_msat = big }
+ @?= Left ValidateHtlcAmounts
+ , testCase "query_channel_range block range" $ do
+ fx <- fixtures
+ let q f n = validate_query_channel_range
+ (QueryChannelRange (fx_chain fx) f n Nothing BOLT1.empty_tlv_stream)
+ q 600000 10000 @?= Right ()
+ q 600000 0 @?= Left ValidateZeroBlocks
+ q maxBound 1 @?= Right ()
+ q maxBound 2 @?= Left ValidateBlockOverflow
+ q 0 maxBound @?= Right ()
+ q 1 maxBound @?= Right ()
+ q 2 maxBound @?= Left ValidateBlockOverflow
+ , testCase "short channel ids ascend" $ do
+ fx <- fixtures
+ let ss = fx_scids fx
+ e = BOLT1.empty_tlv_stream
+ r xs = validate_reply_channel_range
+ (ReplyChannelRange (fx_chain fx) 0 1 True xs Nothing Nothing e)
+ q xs = validate_query_short_channel_ids
+ (QueryShortChannelIds (fx_chain fx) xs Nothing e)
+ r ss @?= Right ()
+ r [] @?= Right ()
+ r (reverse ss) @?= Left ValidateScidNotAscending
+ r (take 1 ss <> take 1 ss) @?= Left ValidateScidNotAscending
+ q ss @?= Right ()
+ q (reverse ss) @?= Left ValidateScidNotAscending
]
--- Mainnet Gossip Tests --------------------------------------------------------
+-- checksums ------------------------------------------------------------------
--- Real mainnet gossip for short_channel_id 850052x2992x1: its
--- channel_announcement and one channel_update per direction. Payloads
--- exclude the 2-byte message type. The digests and checksums were
--- computed independently of this library.
-
-mainnet_tests :: TestTree
-mainnet_tests = testGroup "Mainnet gossip" [
- testCase "fixtures decode from hex" $ do
- BS.length mainnetChanAnn @?= 430
- BS.length mainnetUpdate136 @?= 136
- BS.length mainnetUpdate148 @?= 148
- , testGroup "channel_announcement" [
- testCase "decodes" $ do
- (ann, rest) <- expectRight "decode"
- (decodeChannelAnnouncement mainnetChanAnn)
- rest @?= BS.empty
- formatScid (channelAnnShortChanId ann) @?= "850052x2992x1"
- channelAnnChainHash ann @?= mainnetChainHash
- getFeatureBits (channelAnnFeatures ann) @?= BS.empty
- getNodeId (channelAnnNodeId1 ann) @?= mainnetNodeId1
- getNodeId (channelAnnNodeId2 ann) @?= mainnetNodeId2
- validateChannelAnnouncement ann @?= Right ()
- , testCase "re-encodes byte-exact" $ do
- (ann, _) <- expectRight "decode"
- (decodeChannelAnnouncement mainnetChanAnn)
- encodeChannelAnnouncement ann @?= Right mainnetChanAnn
- , testCase "signature hash" $
- channelAnnouncementHash mainnetChanAnn @?= Just (hex
- "3dc0e5a431551c44f2afd49cda2d3bf2a5f1b9cc86ef93db8953c17dd1ef4ded")
- , testCase "all four signatures verify" $ do
- (ann, _) <- expectRight "decode"
- (decodeChannelAnnouncement mainnetChanAnn)
- h <- expectJust "hash" (channelAnnouncementHash mainnetChanAnn)
- let nid = getNodeId
- btc = unPoint
- assertBool "node_sig_1" $
- verifySig h (nid (channelAnnNodeId1 ann)) (channelAnnNodeSig1 ann)
- assertBool "node_sig_2" $
- verifySig h (nid (channelAnnNodeId2 ann)) (channelAnnNodeSig2 ann)
- assertBool "bitcoin_sig_1" $
- verifySig h (btc (channelAnnBitcoinKey1 ann))
- (channelAnnBitcoinSig1 ann)
- assertBool "bitcoin_sig_2" $
- verifySig h (btc (channelAnnBitcoinKey2 ann))
- (channelAnnBitcoinSig2 ann)
- assertBool "node_sig_1 does not verify under node_id_2" $
- not (verifySig h (nid (channelAnnNodeId2 ann))
- (channelAnnNodeSig1 ann))
- ]
- , testGroup "channel_update (136 bytes)" [
- testCase "decodes" $ do
- (upd, rest) <- expectRight "decode"
- (decodeChannelUpdate mainnetUpdate136)
- rest @?= BS.empty
- formatScid (chanUpdateShortChanId upd) @?= "850052x2992x1"
- chanUpdateChainHash upd @?= mainnetChainHash
- chanUpdateTimestamp upd @?= 1776493010
- chanUpdateChanFlags upd @?= ChannelFlags NodeTwo Disabled
- chanUpdateCltvExpDelta upd @?= CltvExpiryDelta 80
- chanUpdateHtlcMinMsat upd @?= HtlcMinimumMsat 1000
- chanUpdateFeeBaseMsat upd @?= FeeBaseMsat 1000
- chanUpdateFeeProportional upd @?= FeeProportionalMillionths 800
- chanUpdateHtlcMaxMsat upd @?= HtlcMaximumMsat 2970000000
- validateChannelUpdate upd @?= Right ()
- , testCase "re-encodes byte-exact" $ do
- (upd, _) <- expectRight "decode"
- (decodeChannelUpdate mainnetUpdate136)
- encodeChannelUpdate upd @?= mainnetUpdate136
- , testCase "signature hash" $
- channelUpdateHash mainnetUpdate136 @?= Just (hex
- "5c9960b6fef9fa0d53073895420159e856c7c5f33fdeb694b163a832efe6b8e3")
- , testCase "signature verifies under node_id_2" $ do
- (upd, _) <- expectRight "decode"
- (decodeChannelUpdate mainnetUpdate136)
- h <- expectJust "hash" (channelUpdateHash mainnetUpdate136)
- let sig = chanUpdateSignature upd
- assertBool "node_id_2" (verifySig h mainnetNodeId2 sig)
- assertBool "not node_id_1" (not (verifySig h mainnetNodeId1 sig))
- , testCase "checksum" $
- channelUpdateChecksum mainnetUpdate136 @?= Just 0x1da953bd
- ]
- , testGroup "channel_update (148 bytes, with extension)" [
- testCase "decodes, leaving the extension as rest" $ do
- (upd, rest) <- expectRight "decode"
- (decodeChannelUpdate mainnetUpdate148)
- -- an LND inbound-fee TLV (type 55555) after htlc_maximum_msat
- rest @?= hex "fdd9030800000000ffffffed"
- chanUpdateTimestamp upd @?= 1779047542
- chanUpdateChanFlags upd @?= ChannelFlags NodeOne Enabled
- chanUpdateCltvExpDelta upd @?= CltvExpiryDelta 100
- chanUpdateHtlcMinMsat upd @?= HtlcMinimumMsat 1000
- chanUpdateFeeBaseMsat upd @?= FeeBaseMsat 0
- chanUpdateFeeProportional upd @?= FeeProportionalMillionths 5000
- chanUpdateHtlcMaxMsat upd @?= HtlcMaximumMsat 2970000000
- , testCase "re-encodes the fixed fields byte-exact" $ do
- -- the record does not keep extension bytes
- (upd, rest) <- expectRight "decode"
- (decodeChannelUpdate mainnetUpdate148)
- encodeChannelUpdate upd <> rest @?= mainnetUpdate148
- , testCase "signature hash covers the extension" $
- channelUpdateHash mainnetUpdate148 @?= Just (hex
- "b1989933c26561ab2cdcc49b471aee5f6e0403a5eb7b88a908cb19cfee0556b9")
- , testCase "signature verifies under node_id_1" $ do
- (upd, _) <- expectRight "decode"
- (decodeChannelUpdate mainnetUpdate148)
- h <- expectJust "hash" (channelUpdateHash mainnetUpdate148)
- let sig = chanUpdateSignature upd
- assertBool "node_id_1" (verifySig h mainnetNodeId1 sig)
- assertBool "not node_id_2" (not (verifySig h mainnetNodeId2 sig))
- , testCase "checksum includes the extension" $
- channelUpdateChecksum mainnetUpdate148 @?= Just 0xf0a89fda
- ]
+-- an independent, table-driven CRC-32C
+crc32c_ref :: BS.ByteString -> Word32
+crc32c_ref = xor 0xffffffff . BS.foldl' step 0xffffffff
+ where
+ step c b = (c `shiftR` 8)
+ `xor` table (fromIntegral ((c `xor` fromIntegral b) .&. 0xff))
+ table :: Word32 -> Word32
+ table n = go (8 :: Int) n
+ where
+ go 0 x = x
+ go k x
+ | x .&. 1 == 1 = go (k - 1) ((x `shiftR` 1) `xor` 0x82f63b78)
+ | otherwise = go (k - 1) (x `shiftR` 1)
+
+-- the checksum per BOLT #7: the update without signature and timestamp
+checksum_ref :: BS.ByteString -> Word32
+checksum_ref wire =
+ crc32c_ref (BS.take 40 (BS.drop 64 wire) <> BS.drop 108 wire)
+
+checksum_tests :: TestTree
+checksum_tests = testGroup "checksums" [
+ testCase "reference CRC-32C matches RFC 3720 vectors" $ do
+ crc32c_ref "123456789" @?= 0xe3069283
+ crc32c_ref (BS.replicate 32 0x00) @?= 0x8a9136aa
+ crc32c_ref (BS.replicate 32 0xff) @?= 0x62a8ab43
+ crc32c_ref (BS.pack [0 .. 31]) @?= 0x46dd794e
+ crc32c_ref (BS.pack [31, 30 .. 0]) @?= 0x113fdb5c
+ , testCase "checksum ignores signature and timestamp" $ do
+ fx <- fixtures
+ u <- update_fixture fx
+ sig <- must "signature" (BOLT1.signature (BS.replicate 64 0x11))
+ channel_update_checksum u { cu_signature = sig, cu_timestamp = 1 }
+ @?= channel_update_checksum u
+ assertBool "covers the fees"
+ (channel_update_checksum u { cu_fee_base_msat = 1001 }
+ /= channel_update_checksum u)
+ , testProperty "checksum matches the reference" $
+ forAll gen_channel_update $ \u ->
+ channel_update_checksum u === checksum_ref (encode_channel_update u)
]
--- | Unwrap an Either, failing the test on Left.
-expectRight :: Show e => String -> Either e a -> IO a
-expectRight what =
- either (\e -> assertFailure (what ++ " failed: " ++ show e)) pure
+-- properties -----------------------------------------------------------------
--- | Decode hex (empty on malformed input; the fixture length test
--- catches that).
-hex :: BS.ByteString -> BS.ByteString
-hex = maybe BS.empty id . B16.decode
-
--- | Verify a compact ECDSA signature over a digest.
-verifySig :: BS.ByteString -> BS.ByteString -> Signature -> Bool
-verifySig digest pub sig =
- case (Secp256k1.parse_point pub, Secp256k1.parse_sig (unSignature sig)) of
- (Just p, Just s) -> Secp256k1._verify_ecdsa_no_hash digest p s
- _ -> False
-
-mainnetNodeId1, mainnetNodeId2 :: BS.ByteString
-mainnetNodeId1 = hex
- "02bb5e47ee26aca1654ce092d9d06eac15d953210c737f2220e2d7e54c4f108677"
-mainnetNodeId2 = hex
- "030b828fd1e42549fbc2f1d0960ec3663362c41777d3d87fe1b8e1d4bf063abcaa"
-
-mainnetChanAnn :: BS.ByteString
-mainnetChanAnn = hex $ mconcat
- [ "3c256c270126d2c52ab5464fa959d627e9baec8c76bd2b81d34b47c4efb03290"
- , "40126ea7a0f8d31420976dc28ff684dc63f62620fa10dbac4fe3d417ca5690c1"
- , "b7fe1de61f5dd2b8368886d6c98f625c9110198e57c14fac93d4eff34e19ce5f"
- , "5dc6fef4501e8105c8f9b82d6d0b348f5840864445b071ae7a741b5eb92d2ec0"
- , "ab85681e4e1e85144bbbd8757c14cee92468d2c5ac84345cb28931b6fed5e339"
- , "00ed9dd363db630f31248cfa933a3667cbf87bb78a48a442e81c652686355365"
- , "ff33164c7588f0a753d50c4d52dd4a81c611fa47b4e950dfc9e3379a12ef7834"
- , "781c0900b90f56a5a49893446d03c8d283c3c093e3c8c682ff6cb40d70d9eebc"
- , "00006fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d619000000"
- , "00000cf884000bb0000102bb5e47ee26aca1654ce092d9d06eac15d953210c73"
- , "7f2220e2d7e54c4f108677030b828fd1e42549fbc2f1d0960ec3663362c41777"
- , "d3d87fe1b8e1d4bf063abcaa02bc3f260955062a4f427704edef1864c76d6777"
- , "fe81bed43a5f6297dda1c7ca9203523e164f4f9fbdea978c6570d56b13ca8338"
- , "430e6794ff74cf6aa0dbda71dd2d"
+property_tests :: TestTree
+property_tests = testGroup "properties" [
+ testProperty "real gossip re-encodes exactly" $
+ forAll (elements samples) $ \(t, h) ->
+ case B16.decode h of
+ Nothing -> counterexample "bad hex" False
+ Just p -> fmap encode_message (decode_message t p)
+ === Right (Right p)
+ , testProperty "mutated real gossip re-encodes exactly, if it decodes" $
+ forAll (elements samples) $ \(t, h) ->
+ forAll arbitrary $ \(NonNegative i, w) ->
+ case B16.decode h of
+ Nothing -> counterexample "bad hex" False
+ Just p ->
+ let p' = set_byte (i `mod` BS.length p) w p
+ in case decode_message t p' of
+ Left _ -> property True
+ Right m -> encode_message m === Right p'
+ , testProperty "messages round-trip" $
+ forAll gen_message $ \m -> case encode_message m of
+ Left e -> counterexample (show e) False
+ Right p -> decode_message (message_type m) p === Right m
+ , testProperty "channel_flags accessors" $ \w ->
+ let f = ChannelFlags w
+ in channel_flags (channel_flags_direction f) (channel_flags_status f)
+ === ChannelFlags (w .&. 0x03)
+ , testProperty "message_flags accessors" $ \w ->
+ let f = MessageFlags w
+ in message_flags (message_flags_forwarding f)
+ === MessageFlags (w .&. 0x02 .|. 0x01)
+ , testProperty "query_flags sets exactly its bits" $
+ forAll (sublistOf [minBound .. maxBound]) $ \fs ->
+ map (`has_query_flag` query_flags fs) [minBound .. maxBound]
+ === map (`elem` fs) [minBound .. maxBound]
+ , testProperty "query_option sets exactly its bits" $
+ forAll (sublistOf [minBound .. maxBound]) $ \fs ->
+ map (`has_query_option` query_option fs) [minBound .. maxBound]
+ === map (`elem` fs) [minBound .. maxBound]
]
--- | Direction 1, disabled.
-mainnetUpdate136 :: BS.ByteString
-mainnetUpdate136 = hex $ mconcat
- [ "261e3d1f1b77b4a606c20dbe5b395e35703adc82fd9320b88e892f51547b93e9"
- , "060f793d8cfd93d2063060a692c31749d0eb3dd351f3e4bbc562d00f89472a42"
- , "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000"
- , "0cf884000bb0000169e321d20103005000000000000003e8000003e800000320"
- , "00000000b1069a80"
+-- (type, hex payload) of every embedded real or signed message
+samples :: [(Word16, BS.ByteString)]
+samples =
+ [ (256, av_payload v) | v <- mainnet_announcements ]
+ <> [ (258, uv_payload v) | v <- mainnet_updates ]
+ <> [ (257, fst node_announcement_vector) ]
+
+-- generators -----------------------------------------------------------------
+
+gen_bytes :: Int -> Gen BS.ByteString
+gen_bytes n = BS.pack <$> vectorOf n arbitrary
+
+gen_var_bytes :: Int -> Gen BS.ByteString
+gen_var_bytes m = choose (0, m) >>= gen_bytes
+
+gen_chain_hash :: Gen ChainHash
+gen_chain_hash = gen_bytes 32 `suchThatMap` BOLT1.chain_hash
+
+gen_signature :: Gen Signature
+gen_signature = gen_bytes 64 `suchThatMap` BOLT1.signature
+
+gen_point :: Gen Point
+gen_point = do
+ h <- elements [0x02, 0x03]
+ b <- gen_bytes 32
+ pure (BS.cons h b) `suchThatMap` BOLT1.point
+
+gen_scid :: Gen ShortChannelId
+gen_scid = ShortChannelId <$> arbitrary
+
+gen_msat :: Gen BOLT1.MilliSatoshi
+gen_msat = choose (0, 0x1d24b2dfac520000) `suchThatMap` BOLT1.milli_satoshi
+
+gen_features :: Gen BOLT9.FeatureVector
+gen_features = BOLT9.parse <$> gen_var_bytes 8
+
+-- TLV records of distinct odd types at or above the given minimum
+gen_tlvs :: Word64 -> Gen TlvStream
+gen_tlvs lo = do
+ n <- choose (0, 3)
+ ts <- vectorOf n (choose (lo `div` 2, 40000))
+ rs <- traverse (\t -> TlvRecord (2 * t + 1) <$> gen_var_bytes 10) ts
+ pure rs `suchThatMap` BOLT1.tlv_stream
+
+gen_address :: Gen Address
+gen_address = oneof
+ [ AddrIPv4 <$> (gen_bytes 4 `suchThatMap` ipv4_addr) <*> arbitrary
+ , AddrIPv6 <$> (gen_bytes 16 `suchThatMap` ipv6_addr) <*> arbitrary
+ , AddrTorV2 <$> (gen_bytes 12 `suchThatMap` tor_v2_addr)
+ , AddrTorV3 <$> (gen_bytes 35 `suchThatMap` tor_v3_addr) <*> arbitrary
+ , AddrDNS <$> (gen_var_bytes 40 `suchThatMap` hostname) <*> arbitrary
]
--- | Direction 0, enabled, with a 12-byte extension.
-mainnetUpdate148 :: BS.ByteString
-mainnetUpdate148 = hex $ mconcat
- [ "1c3f0859cb3f4494919c3dcc80d22bbf1e25e378bd95f071530833c2371c33d7"
- , "0766012ebef1cd79b299a54d0d29bea2ef6feca1d6cfa188864c95d7eea7e9a4"
- , "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000"
- , "0cf884000bb000016a0a1c760100006400000000000003e80000000000001388"
- , "00000000b1069a80fdd9030800000000ffffffed"
+gen_unknown_addresses :: Gen BS.ByteString
+gen_unknown_addresses = oneof
+ [ pure BS.empty
+ , BS.cons <$> elements (0 : [6 .. 255]) <*> gen_var_bytes 10
]
--- Property Tests --------------------------------------------------------------
-
-property_tests :: TestTree
-property_tests = testGroup "Properties" [
- testProperty "ChannelAnnouncement roundtrip" propChannelAnnouncementRoundtrip
- , testProperty "ChannelUpdate roundtrip" propChannelUpdateRoundtrip
- , testProperty "AnnouncementSignatures roundtrip"
- propAnnouncementSignaturesRoundtrip
- , testProperty "GossipTimestampFilter roundtrip"
- propGossipTimestampFilterRoundtrip
- , testProperty "ChannelFlags encode/decode roundtrip"
- propChannelFlagsRoundtrip
- , testProperty "ShortChannelId bytes roundtrip"
- propScidBytesRoundtrip
- , testProperty "Hostname validates length bounds"
- propHostnameValidation
- , testProperty "SCID list encode/decode roundtrip"
- propScidListRoundtrip
+gen_channel_announcement :: Gen ChannelAnnouncement
+gen_channel_announcement = ChannelAnnouncement
+ <$> gen_signature <*> gen_signature <*> gen_signature <*> gen_signature
+ <*> gen_features <*> gen_chain_hash <*> gen_scid
+ <*> gen_point <*> gen_point <*> gen_point <*> gen_point
+ <*> gen_tlvs 0
+
+gen_node_announcement :: Gen NodeAnnouncement
+gen_node_announcement = NodeAnnouncement
+ <$> gen_signature <*> gen_features <*> arbitrary <*> gen_point
+ <*> (RgbColor <$> arbitrary <*> arbitrary <*> arbitrary)
+ <*> (gen_bytes 32 `suchThatMap` alias)
+ <*> listOf gen_address <*> gen_unknown_addresses <*> gen_tlvs 0
+
+gen_channel_update :: Gen ChannelUpdate
+gen_channel_update = ChannelUpdate
+ <$> gen_signature <*> gen_chain_hash <*> gen_scid <*> arbitrary
+ <*> (MessageFlags <$> arbitrary) <*> (ChannelFlags <$> arbitrary)
+ <*> arbitrary <*> gen_msat <*> arbitrary <*> arbitrary <*> gen_msat
+ <*> gen_tlvs 0
+
+-- a list of short channel ids and, maybe, one entry for each
+gen_scids_with :: Gen a -> Gen ([ShortChannelId], Maybe [a])
+gen_scids_with g = do
+ ss <- listOf gen_scid
+ xs <- oneof [pure Nothing, Just <$> vectorOf (length ss) g]
+ pure (ss, xs)
+
+gen_message :: Gen Message
+gen_message = oneof
+ [ MsgChannelAnnouncement <$> gen_channel_announcement
+ , MsgNodeAnnouncement <$> gen_node_announcement
+ , MsgChannelUpdate <$> gen_channel_update
+ , MsgAnnouncementSignatures <$> (AnnouncementSignatures
+ <$> (gen_bytes 32 `suchThatMap` BOLT1.channel_id) <*> gen_scid
+ <*> gen_signature <*> gen_signature <*> gen_tlvs 0)
+ , MsgQueryShortChannelIds <$> do
+ (ss, fs) <- gen_scids_with (QueryFlags <$> arbitrary)
+ QueryShortChannelIds <$> gen_chain_hash <*> pure ss <*> pure fs
+ <*> gen_tlvs 3
+ , MsgReplyShortChannelIdsEnd <$> (ReplyShortChannelIdsEnd
+ <$> gen_chain_hash <*> arbitrary <*> gen_tlvs 0)
+ , MsgQueryChannelRange <$> (QueryChannelRange
+ <$> gen_chain_hash <*> arbitrary <*> arbitrary
+ <*> oneof [pure Nothing, Just . QueryOption <$> arbitrary]
+ <*> gen_tlvs 3)
+ , MsgReplyChannelRange <$> do
+ (ss, ts) <- gen_scids_with
+ (ChannelUpdateTimestamps <$> arbitrary <*> arbitrary)
+ cs <- oneof [pure Nothing, Just <$> vectorOf (length ss)
+ (ChannelUpdateChecksums <$> arbitrary <*> arbitrary)]
+ ReplyChannelRange <$> gen_chain_hash <*> arbitrary <*> arbitrary
+ <*> arbitrary <*> pure ss <*> pure ts <*> pure cs <*> gen_tlvs 5
+ , MsgGossipTimestampFilter <$> (GossipTimestampFilter
+ <$> gen_chain_hash <*> arbitrary <*> arbitrary <*> gen_tlvs 0)
]
-
--- Property: ChannelAnnouncement roundtrip
-propChannelAnnouncementRoundtrip :: Property
-propChannelAnnouncementRoundtrip = property $ do
- let msg = ChannelAnnouncement
- { channelAnnNodeSig1 = testSignature
- , channelAnnNodeSig2 = testSignature
- , channelAnnBitcoinSig1 = testSignature
- , channelAnnBitcoinSig2 = testSignature
- , channelAnnFeatures = emptyFeatures
- , channelAnnChainHash = testChainHash
- , channelAnnShortChanId = testShortChannelId
- , channelAnnNodeId1 = testNodeId
- , channelAnnNodeId2 = testNodeId2
- , channelAnnBitcoinKey1 = testPoint
- , channelAnnBitcoinKey2 = testPoint
- }
- case encodeChannelAnnouncement msg of
- Left _ -> False
- Right encoded -> case decodeChannelAnnouncement encoded of
- Right (decoded, _) -> decoded == msg
- Left _ -> False
-
--- Property: ChannelUpdate roundtrip
-propChannelUpdateRoundtrip :: Word32 -> Word16 -> Property
-propChannelUpdateRoundtrip timestamp cltvDelta = property $ do
- let msg = ChannelUpdate
- { chanUpdateSignature = testSignature
- , chanUpdateChainHash = testChainHash
- , chanUpdateShortChanId = testShortChannelId
- , chanUpdateTimestamp = timestamp
- , chanUpdateChanFlags = ChannelFlags
- { cfDirection = NodeOne
- , cfStatus = Enabled }
- , chanUpdateCltvExpDelta = CltvExpiryDelta cltvDelta
- , chanUpdateHtlcMinMsat = HtlcMinimumMsat 1000
- , chanUpdateFeeBaseMsat = FeeBaseMsat 1000
- , chanUpdateFeeProportional = FeeProportionalMillionths 100
- , chanUpdateHtlcMaxMsat = HtlcMaximumMsat 1000000000
- }
- encoded = encodeChannelUpdate msg
- case decodeChannelUpdate encoded of
- Right (decoded, _) -> decoded == msg
- Left _ -> False
-
--- Property: AnnouncementSignatures roundtrip
-propAnnouncementSignaturesRoundtrip :: Property
-propAnnouncementSignaturesRoundtrip = property $ do
- let msg = AnnouncementSignatures
- { annSigChannelId = testChannelId
- , annSigShortChanId = testShortChannelId
- , annSigNodeSig = testSignature
- , annSigBitcoinSig = testSignature
- }
- encoded = encodeAnnouncementSignatures msg
- case decodeAnnouncementSignatures encoded of
- Right (decoded, _) -> decoded == msg
- Left _ -> False
-
--- Property: GossipTimestampFilter roundtrip
-propGossipTimestampFilterRoundtrip :: Word32 -> Word32 -> Property
-propGossipTimestampFilterRoundtrip firstTs tsRange = property $ do
- let msg = GossipTimestampFilter
- { gossipFilterChainHash = testChainHash
- , gossipFilterFirstTimestamp = firstTs
- , gossipFilterTimestampRange = tsRange
- }
- encoded = encodeGossipTimestampFilter msg
- case decodeGossipTimestampFilter encoded of
- Right (decoded, _) -> decoded == msg
- Left _ -> False
-
--- Property: ChannelFlags encode/decode roundtrip
-propChannelFlagsRoundtrip :: Property
-propChannelFlagsRoundtrip =
- forAll genChannelFlags $ \cf ->
- decodeChannelFlags (encodeChannelFlags cf) == cf
- where
- genChannelFlags :: Gen ChannelFlags
- genChannelFlags = ChannelFlags
- <$> elements [NodeOne, NodeTwo]
- <*> elements [Enabled, Disabled]
-
--- Property: ShortChannelId bytes roundtrip
-propScidBytesRoundtrip :: Property
-propScidBytesRoundtrip =
- forAll (choose (0, 0xFFFFFF)) $ \bh ->
- forAll (choose (0, 0xFFFFFF)) $ \ti ->
- forAll arbitrary $ \oi ->
- case shortChannelId bh ti oi of
- Nothing -> False
- Just scid ->
- scidFromBytes (scidToBytes scid)
- == Just scid
-
--- Property: Hostname validates length bounds
-propHostnameValidation :: NonNegative Int -> Property
-propHostnameValidation (NonNegative n) = property $
- let len = n `mod` 300
- bs = BS.replicate len 0x61
- in case hostname bs of
- Just _ -> len >= 1 && len <= 255
- Nothing -> len == 0 || len > 255
-
--- Property: SCID list encode/decode roundtrip
-propScidListRoundtrip :: Property
-propScidListRoundtrip =
- forAll (listOf genScid) $ \scids ->
- case decodeShortChannelIdList
- (encodeShortChannelIdList scids) of
- Right decoded -> decoded == scids
- Left _ -> False
- where
- genScid :: Gen ShortChannelId
- genScid = do
- bh <- choose (0, 0xFFFFFF)
- ti <- choose (0, 0xFFFFFF)
- oi <- arbitrary
- case shortChannelId bh ti oi of
- Just s -> pure s
- Nothing -> pure testShortChannelId
diff --git a/test/Vectors.hs b/test/Vectors.hs
@@ -0,0 +1,376 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+-- |
+-- Module: Vectors
+-- Copyright: (c) 2025 Jared Tobin
+-- License: MIT
+-- Maintainer: Jared Tobin <jared@ppad.tech>
+--
+-- Test vectors: real mainnet gossip, and the extended gossip query
+-- vectors of BOLT #7 (bolt07/extended-queries.json). Hex strings.
+
+module Vectors (
+ AnnVector(..)
+ , UpdateVector(..)
+ , mainnet_announcements
+ , mainnet_updates
+ , node_announcement_vector
+ , query_vectors
+ ) where
+
+import qualified Data.ByteString as BS
+import Data.Word (Word32)
+
+-- mainnet gossip --------------------------------------------------------------
+
+-- Real mainnet channel_announcements and channel_updates, as captured
+-- from a peer. Payloads exclude the 2-byte message type. The digests
+-- (double SHA-256 of the signed bytes) and CRC-32C checksums were
+-- computed independently of this library.
+
+-- | A channel_announcement payload and its signature digest.
+data AnnVector = AnnVector
+ { av_payload :: !BS.ByteString -- ^ hex
+ , av_digest :: !BS.ByteString -- ^ hex
+ }
+
+-- | A channel_update payload, its signature digest and checksum, and
+-- the index of its channel's announcement in 'mainnet_announcements'.
+data UpdateVector = UpdateVector
+ { uv_payload :: !BS.ByteString -- ^ hex
+ , uv_digest :: !BS.ByteString -- ^ hex
+ , uv_checksum :: !Word32
+ , uv_ann :: !Int
+ }
+
+mainnet_announcements :: [AnnVector]
+mainnet_announcements = [
+ -- 850052x2992x1
+ AnnVector (mconcat
+ [ "3c256c270126d2c52ab5464fa959d627e9baec8c76bd2b81d34b47c4efb03290"
+ , "40126ea7a0f8d31420976dc28ff684dc63f62620fa10dbac4fe3d417ca5690c1"
+ , "b7fe1de61f5dd2b8368886d6c98f625c9110198e57c14fac93d4eff34e19ce5f"
+ , "5dc6fef4501e8105c8f9b82d6d0b348f5840864445b071ae7a741b5eb92d2ec0"
+ , "ab85681e4e1e85144bbbd8757c14cee92468d2c5ac84345cb28931b6fed5e339"
+ , "00ed9dd363db630f31248cfa933a3667cbf87bb78a48a442e81c652686355365"
+ , "ff33164c7588f0a753d50c4d52dd4a81c611fa47b4e950dfc9e3379a12ef7834"
+ , "781c0900b90f56a5a49893446d03c8d283c3c093e3c8c682ff6cb40d70d9eebc"
+ , "00006fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d619000000"
+ , "00000cf884000bb0000102bb5e47ee26aca1654ce092d9d06eac15d953210c73"
+ , "7f2220e2d7e54c4f108677030b828fd1e42549fbc2f1d0960ec3663362c41777"
+ , "d3d87fe1b8e1d4bf063abcaa02bc3f260955062a4f427704edef1864c76d6777"
+ , "fe81bed43a5f6297dda1c7ca9203523e164f4f9fbdea978c6570d56b13ca8338"
+ , "430e6794ff74cf6aa0dbda71dd2d"
+ ])
+ "3dc0e5a431551c44f2afd49cda2d3bf2a5f1b9cc86ef93db8953c17dd1ef4ded"
+ -- 850013x1067x1
+ , AnnVector (mconcat
+ [ "f37bf5c2f2c11c79541fe16c2a5d63b8795d583ba72f8f1ec9160bbe02a7101d"
+ , "519aa61c125737f82a67be9a586da3e04f941907d9a9c59dd77dd2ae965df2cd"
+ , "f3e247f3f0227ef24baa2ede50f1844e12db674ac4ea53644d701521951ab3c5"
+ , "49eb93a75d6984cb5ca7a5872f0a27f6aa3608a5dfe4468e3f941af8c86425d3"
+ , "4c1ac5663d72b2934d74b20a2f364f3cd09c6528d47e09f9be99be54b1338b0e"
+ , "6bf9ee1bad5bf92cc95de7b0d268d92e9c671f2cfe51a0f5edbf93bf7ee9232e"
+ , "20e7d157eefa4b3615e176f61cad5543584188c9d890209f192c0118f162c77f"
+ , "4259dc4c6aec293913bd59211a9a876cad93d7f7961e027de731e48171688ddb"
+ , "00006fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d619000000"
+ , "00000cf85d00042b00010322d0e43b3d92d30ed187f4e101a9a9605c3ee5fc97"
+ , "21e6dac3ce3d7732fbb13e037f990e61acee8a7697966afd29dd88f3b1f8a7b1"
+ , "4d625c4f8742bd952003a59003359448149bdca64024ab6c2243ab555f9313aa"
+ , "49ee1da0b1dd0ce1c575b2cca202dfb90cceb72720dd3d3ea168e53ed64d506e"
+ , "4c277fde46946843ee8eab54ab3b"
+ ])
+ "f753e841682abc578fd12650fe47dac6094ab7882d47026bd3b6055e9060e993"
+ -- 850043x3419x1
+ , AnnVector (mconcat
+ [ "0269559b5385e499d1ec6283c2ec0a0a5b93d45f6cce2d5f69709a9fd3382722"
+ , "063a230e7c0fca1b198ed81fc36658c259f70475074f336c7d146d7cb4042de2"
+ , "74162681acf44507e98656d6311fc2a8a85ef7bebd34fde195c244623704b8bf"
+ , "77d8e2965bdbc7ae483d8d2e2ef41cee147ada06440c1501af0a502f54f8d060"
+ , "3ed003cb101583b0986c54994f985e88b730f80a600706b6ac1de2b8030e3961"
+ , "388ad7d4a6d7b0d2718374f52ef4b4ab7f7523ba839d922f362613a283615328"
+ , "54b9165b22d4f86be8f7503f0149b36db4bbba3496bcb6fb989c558bea04c2d0"
+ , "1e6fbddfbec938a1c9fdd841339b7f5ecc40e33cc1df7da7b76da52f72078e7d"
+ , "00006fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d619000000"
+ , "00000cf87b000d5b0001020b831768ded7615eef25b34639c14b912496137ff5"
+ , "218ad3277d0efd57a32e0902bb5e47ee26aca1654ce092d9d06eac15d953210c"
+ , "737f2220e2d7e54c4f10867703a9be624c84cde9e7452b108942cb7c3c642a1c"
+ , "37ab6468b031c605bb103cb5ab038ace825c070bc5af8d326b534ef6ab38ec04"
+ , "adbbfa859b00369cc86fbacc3e38"
+ ])
+ "473c18a52a826efc39367a38a98d9226511289f7e33c38906faeecf890ecdf0f"
+ -- 850292x3675x0
+ , AnnVector (mconcat
+ [ "76d849642499e76439884821fe95628a274e059a77a5cad5039836d40a63cc52"
+ , "04aa8748f7a284eaf0e22450d37b51feb293391ae2460ce328826af35ab9da8e"
+ , "e052bd9578c74e5a0180294141d66f8f78ec2928456db4eab4062f31c1666bd9"
+ , "30a220d211b8a438f9ad9c9169e3bd197ab1452b784f91f22529d4144fc19c28"
+ , "c0a83de0ec63b53e71f119b37983f687ee947521b8c9d044917fbb8a0e9a8c2a"
+ , "26ac05a821fdf1bac3a764ab9f08c293ed0b01e6dad17ad4da991b403cdfe576"
+ , "d49813544db805ae2afbe85572915e4145df0dc55cfd15396bd6f4abbbc9a052"
+ , "4e8577ed60f5d058706ca9e3fb970d54b707c598e258727b8da2df2a168f4f42"
+ , "00006fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d619000000"
+ , "00000cf974000e5b0000020cd843f3128386ac5551647496d57aeb0b5259611d"
+ , "7ebb9314cae7711565354803bd743abf0f94b6d208d1c3c47aa8e958d6ddd8aa"
+ , "4e8c910c6e1d6b8dd7413a93024c95a299224252655af8a9a8d07da1313b8efc"
+ , "1dda8964d652ccb81ac5253e0d039404ec864e4efc233af1c781c872048493bc"
+ , "df6d284746293cf5f2b2790af0de"
+ ])
+ "d0f087817bde3d17c5c6f9eb5a9d7c0d14f2498d0c58ebfbd4e8bccfa536ff05"
+ -- 853240x888x2
+ , AnnVector (mconcat
+ [ "f4e2a166440900d15e319812df9c5ffeab61e15f417febb18c1fc07dcc701700"
+ , "3fecf1c075d39f4e134e5061a00de59161378f6855cefa71687f710678cc4c27"
+ , "e010bc600453f7ac4b84c5f5243dc2b6447af512110c1ef11173a1498a71bc6f"
+ , "535607ccf9f64e87ea9641413bd3ad2ec7d96d711f5de0106bf23fd172215f18"
+ , "fd12d34bffebe0d64900e944b4fd7ad81f26adb5ed4ea0099ef3c53e09034178"
+ , "693af5a55308d341e8af0dddd19eed46438174cdc2e9c65f9ff14844c4d991c4"
+ , "37f97805f017d56205f387b24f22d620ea474ba6f837ae114cef015022d55841"
+ , "2eca08400137adc856e80254b61783a77941f9550a0d008ef567c0acfa8e9155"
+ , "00006fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d619000000"
+ , "00000d04f8000378000202ad898fce14604311ebe871e1053fa248a25e683803"
+ , "a8e599f168fba79bad5f5202c2ecfd7e7646f7a249491c565737b5c9f83863c2"
+ , "3b88e752f2d624e026f5fde702519af7e8fa7c6c47858108d1ab7eb9b531a594"
+ , "34314c804174ccb0bb658d0f5e03562b849da8a6e3f17d1585f53011eabeb53d"
+ , "b6eb23066af87bb81e8adbd0eb1a"
+ ])
+ "cd04212b8a16a7367adb8314e37698b50ad54a9c901d21586fdd1420fba78431"
+ -- 855691x1739x0
+ , AnnVector (mconcat
+ [ "2bd9078e1b73e459703af26014f26f43426961f70e0f289545559bcc5f75cd74"
+ , "5dc4beba17a15301df594267c92fd9e563d75982cc406e095162bdd931107d84"
+ , "e98528d5322f18e203805b372049a2eb32aadd3a26ad2fa733a06279635e8dca"
+ , "4d778a9c4a1ae260ae581562bd5e57298db70e48308ee2ba8c8bdb91db062e21"
+ , "41e5a4396bdc112449c9d2f572f88c1fa610620825ac3fa6bb04a1bfd6cc34a4"
+ , "5fb5a00ec08371146305eb845d2ea98b4f60c8bff4e5e589315c34c6af95863e"
+ , "6c01bdbd7c589bc448b4f0805e9756b2dcfcd5812df8b507cbf2ae488d6519a3"
+ , "12841bcf0f3707f1934b00f845cbe05b67d085d1a4367c8820659285e8760952"
+ , "00006fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d619000000"
+ , "00000d0e8b0006cb000002439112f98b6f4828e1a7426bfbece166bd36837d7d"
+ , "ea4225a75ebd1d96864a070324ba2392e25bff76abd0b1f7e4b53b5f82aa53fd"
+ , "dc3419b051b6c801db9e2247039adc288b1728f289ca4ac30e4b258a19f776c2"
+ , "0e3190f259d759cd672c5f943803e45a0234b0649ba3d599e10df0339a36c140"
+ , "652b5efa18b7c17fadfd5b754888"
+ ])
+ "0092fb593a97482cd7a0dc9ed4954e90eb1ca0700ca55f7a0183674a5faf27ea"
+ ]
+
+mainnet_updates :: [UpdateVector]
+mainnet_updates = [
+ -- 850052x2992x1, 148 bytes, channel_flags 0
+ UpdateVector (mconcat
+ [ "1c3f0859cb3f4494919c3dcc80d22bbf1e25e378bd95f071530833c2371c33d7"
+ , "0766012ebef1cd79b299a54d0d29bea2ef6feca1d6cfa188864c95d7eea7e9a4"
+ , "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000"
+ , "0cf884000bb000016a0a1c760100006400000000000003e80000000000001388"
+ , "00000000b1069a80fdd9030800000000ffffffed"
+ ])
+ "b1989933c26561ab2cdcc49b471aee5f6e0403a5eb7b88a908cb19cfee0556b9"
+ 0xf0a89fda 0
+ -- 850052x2992x1, 136 bytes, channel_flags 3
+ , UpdateVector (mconcat
+ [ "261e3d1f1b77b4a606c20dbe5b395e35703adc82fd9320b88e892f51547b93e9"
+ , "060f793d8cfd93d2063060a692c31749d0eb3dd351f3e4bbc562d00f89472a42"
+ , "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000"
+ , "0cf884000bb0000169e321d20103005000000000000003e8000003e800000320"
+ , "00000000b1069a80"
+ ])
+ "5c9960b6fef9fa0d53073895420159e856c7c5f33fdeb694b163a832efe6b8e3"
+ 0x1da953bd 0
+ -- 850013x1067x1, 136 bytes, channel_flags 1
+ , UpdateVector (mconcat
+ [ "7619128817aadc3c4da18103c468f8c0869f03394e815a7386a81bf74eabb76f"
+ , "357c67e0353b25b173bb67a57185db0d39b8bc3736538e58376ccd184cadf539"
+ , "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000"
+ , "0cf85d00042b000169e334b80101005000000000000003e8000003e800000001"
+ , "00000022ce4c6080"
+ ])
+ "b3d7e24cfbf4d578e3d8ded39d2885fec322c57355e7a513879c72665014e094"
+ 0xb28659fc 1
+ -- 850043x3419x1, 136 bytes, channel_flags 0
+ , UpdateVector (mconcat
+ [ "f0b2facf82a20cb2c33b12337fd54f4553decff2abc86c20fb7059b890d2f16f"
+ , "11d604798b3829ba0367c1898ea21fbb0d50570e70812119ecc069ecde21785a"
+ , "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000"
+ , "0cf87b000d5b000169e3810f0100006400000000000f424000000000000000c3"
+ , "000000001d8119c0"
+ ])
+ "1b2702991ef2d85cc3dffdc390a3a597f3651d00803e6e08581fd8140694ddf4"
+ 0x036645d4 2
+ -- 850043x3419x1, 148 bytes, channel_flags 1
+ , UpdateVector (mconcat
+ [ "c93858ef07309fe98c1690521634ad3a55c715500cdf4d31eef8d6f412613c0a"
+ , "491df28ec2e7c4a5288a5bf2848d18486d9cf4eeb3f41befeaedf1e121416438"
+ , "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000"
+ , "0cf87b000d5b000169e3810f0101006400000000000003e80000000000001388"
+ , "000000009ae5c730fdd9030800000000fffffa95"
+ ])
+ "6deeb53e1859601e9b2b859dccace2dd9f85dc6d0605d6d7a966329d2633df1c"
+ 0x878e5ea4 2
+ -- 850052x2992x1, 148 bytes, channel_flags 2
+ , UpdateVector (mconcat
+ [ "3bf18f613f864d7b9907c98915cf4cbec8d4ac5245f5c66ca3d93571074d8c4f"
+ , "3cbc7963377020145f9645ead56f462537384e43cba27ac4b73d9e734275fe6a"
+ , "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000"
+ , "0cf884000bb0000169e3478f0102006400000000000003e80000000000001066"
+ , "00000000b1069a80fdd9030800000000fffffab8"
+ ])
+ "3af8a43926c24d2707cecf621fb7906cc1feb4c6880b0fde3de19bb5da7e5649"
+ 0x5ba7913c 0
+ -- 850292x3675x0, 136 bytes, channel_flags 2
+ , UpdateVector (mconcat
+ [ "ee28b4d0ce3abded3c43d5d990a40020b255df072cc76a9fdfd8482aed0c81c1"
+ , "02e7e124dcd74acaaf5cf0773cbd10a828b721925cb54f7569cca335f6a9237c"
+ , "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000"
+ , "0cf974000e5b000069e37c460102002800000000000003e8000003e800000001"
+ , "0000000076046700"
+ ])
+ "43949a9fa1993b3375b7fc2e3a35e35ceb28766907f3d738fa8f46b9ba02474f"
+ 0x9d4f0032 3
+ -- 853240x888x2, 148 bytes, channel_flags 3
+ , UpdateVector (mconcat
+ [ "c5cfd6f873caf8ad9207f10db0b618bbf4db86e31b64ad6d0d2aca0a14de8899"
+ , "261b3b94e7eff8317f77c3fee62941e660361dd3ed4213a94a7bb13fee71dcc8"
+ , "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000"
+ , "0d04f8000378000269e32992010300900000000000005208000000000000017a"
+ , "000000001dcd6500fdd903080000000000000000"
+ ])
+ "a6d2ea4523ee28ad1f117ac2cffbb6bf29aa5cc6f7f58d2dc058a999563c9560"
+ 0x172a10e7 4
+ -- 855691x1739x0, 136 bytes, channel_flags 0
+ , UpdateVector (mconcat
+ [ "0f325a044bff29a14636b22023d2a44abf939e34b776020907627b4c72488ed9"
+ , "70b357c059e7a110611bb2093d9fffdd00028a84fc08aea155a57d4b79434410"
+ , "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000"
+ , "0d0e8b0006cb000069e046c70100004800000000000003e80000000000000147"
+ , "0000000000000000"
+ ])
+ "d00785fd5793d86401fb54de019d4094fbff37416d9f611a55fc8fe9a5f536c4"
+ 0x368d3c11 5
+ -- 850013x1067x1, 148 bytes, channel_flags 0
+ , UpdateVector (mconcat
+ [ "2faeaef5708bde4ce33a2145bca38544d9d3c4431c6e10210f6fab36c37306f2"
+ , "53ab90c2d5ec3710c6f481dab4735e398fa83eec94b2d5a9bd41df26a92bce7b"
+ , "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000"
+ , "0cf85d00042b000169e33b320100009000000000000003e8000000000000109a"
+ , "00000022ce4c6080fdd903080000000000000000"
+ ])
+ "1acb192f08b4dfb70359cebc337e6ed2cc985b74e70dacc9ecdc1bd957a56923"
+ 0xf4948aaf 1
+ -- 850292x3675x0, 136 bytes, channel_flags 1
+ , UpdateVector (mconcat
+ [ "6bbb96e7af06e5a35815d7bfc1777ee680cb7d2f728855cc8373baf469e1d375"
+ , "3aa3f7023a796ead27e8fc37e9509358c6e8c2e9ee6a71db5c8dc9c044a3395c"
+ , "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000"
+ , "0cf974000e5b000069e37c840101005000000000000003e80000000000000064"
+ , "0000000076046700"
+ ])
+ "cf3744188962bb9ab7a9847f9b21624c488ec250e780c728c1f9d0032d2ff3a2"
+ 0xe7e9397e 3
+ -- 853240x888x2, 136 bytes, channel_flags 0
+ , UpdateVector (mconcat
+ [ "49ac69d15fbdb35e1022bbe10d4c319fcc981c488e8120a6c6630caa81d96326"
+ , "335ac4948bbaa6bd24811550700c98fcb6af26f00f173d94fa1f060962e8d829"
+ , "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000"
+ , "0d04f8000378000269dea9690100005000000000000003e8000003e8000003e7"
+ , "00000000ec08ce00"
+ ])
+ "14e2befbe15ea11b5094628ce2ceb1e22a325ed7a7fc03300641e0d7785e32f1"
+ 0xe265c205 4
+ -- 855691x1739x0, 136 bytes, channel_flags 1
+ , UpdateVector (mconcat
+ [ "337910ec557c3c04ad42130175e9c783179bb6e0627b36e96b5c635795e4f500"
+ , "2f11d94319a52c930fd166643709f1b97c096c64cbf412f620f53158c45054eb"
+ , "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000"
+ , "0d0e8b0006cb000069e2cb460101005000000000000003e8000000000000000a"
+ , "000000012a05f200"
+ ])
+ "bc08450188a2eb2f1e4fa964a5f11d458a06ce4a00010f86980fbb68c7d8f033"
+ 0x822c67ea 5
+ ]
+
+-- node_announcement ----------------------------------------------------------
+
+-- | A node_announcement payload signed with the secret key 0x4242..42,
+-- and its signature digest (computed independently of this library).
+-- It carries one descriptor of each known type, a descriptor of the
+-- unknown type 6, and an extension record of type 101.
+node_announcement_vector :: (BS.ByteString, BS.ByteString)
+node_announcement_vector = (mconcat
+ [ "edbd2df19aee2f072d9665e2d9a2807fbcd4be6876f6863955ea11870e411e66"
+ , "063a31dcef14f1983524f87e108ee556b3a6646c873cd4bbcc07512c8f5e8f6a"
+ , "000908000000000002aaa26553f1000324653eac434488002cc06bbfb7f10fe1"
+ , "8991e35f9fe4302dbea6d2353dc0ab1c314159707061642d626f6c7437000000"
+ , "000000000000000000000000000000000000000063017f000001260702000000"
+ , "00000000000000000000000001260703aaaaaaaaaaaaaaaaaaaaaaaa04bbbbbb"
+ , "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
+ , "2607050e6c6e2e6578616d706c652e636f6d2607060102036503657874"
+ ]
+ , "c81748f1075dce2da919ce8a52fe59fec409aa10208b8ffc227f5292af5f0abf")
+
+-- extended gossip queries ----------------------------------------------------
+
+-- | The vectors of bolt07/extended-queries.json, in order: messages with
+-- their 2-byte type, in hex.
+query_vectors :: [BS.ByteString]
+query_vectors = [
+ -- 0: QueryChannelRange
+ mconcat
+ [ "01070f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e"
+ , "2206000186a0000005dc"
+ ]
+ -- 1: QueryChannelRange
+ , mconcat
+ [ "01070f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e"
+ , "2206000088b800000064010103"
+ ]
+ -- 2: ReplyChannelRange, UNCOMPRESSED
+ , mconcat
+ [ "01080f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e"
+ , "2206000b8a06000005dc01001900000000000000008e0000000000003c690000"
+ , "00000045a6c4"
+ ]
+ -- 3: ReplyChannelRange, COMPRESSED_ZLIB
+ , mconcat
+ [ "01080f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e"
+ , "2206000006400000006e01001601789c636000833e08659309a65878be010010"
+ , "a9023a"
+ ]
+ -- 4: ReplyChannelRange, UNCOMPRESSED
+ , mconcat
+ [ "01080f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e"
+ , "22060001ddde000005dc01001900000000000000304300000000000778d60000"
+ , "00000046e1c1011900000282c1000e77c5000778ad00490ab00000b57800955b"
+ , "ff031800000457000008ae00000d050000115c000015b300001a0a"
+ ]
+ -- 5: ReplyChannelRange, COMPRESSED_ZLIB
+ , mconcat
+ [ "01080f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e"
+ , "22060001ddde000005dc01001801789c63600001036730c55e710d4cbb3d3c08"
+ , "0017c303b1012201789c63606a3ac8c0577e9481bd622d8327d7060686ad150c"
+ , "53a3ff0300554707db031800000457000008ae00000d050000115c000015b300"
+ , "001a0a"
+ ]
+ -- 6: QueryShortChannelIds, UNCOMPRESSED
+ , mconcat
+ [ "01050f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e"
+ , "2206001900000000000000008e0000000000003c69000000000045a6c4"
+ ]
+ -- 7: QueryShortChannelIds, COMPRESSED_ZLIB
+ , mconcat
+ [ "01050f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e"
+ , "2206001801789c63600001c12b608a69e73e30edbaec0800203b040e"
+ ]
+ -- 8: QueryShortChannelIds, UNCOMPRESSED
+ , mconcat
+ [ "01050f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e"
+ , "22060019000000000000002fc80000000000003cc4000000000045a6c4010c01"
+ , "789c6364620100000e0008"
+ ]
+ -- 9: QueryShortChannelIds, COMPRESSED_ZLIB
+ , mconcat
+ [ "01050f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e"
+ , "2206001801789c63600001f30a30c5b0cd144cb92e3b020017c6034a010c0178"
+ , "9c6364620100000e0008"
+ ]
+ ]