bolt7

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

commit 69a62c710f6da9d968d67d4876bb0d771fd017dc
parent cf2177ebbb8c31b41f49fd99be95d597c7ffe08d
Author: Jared Tobin <jared@jtobin.io>
Date:   Sun, 25 Jan 2026 16:04:13 +0400

Refactor: use newtypes for routing parameters

Convert type aliases to newtypes for improved type safety:
- CltvExpiryDelta (Word16)
- FeeBaseMsat (Word32)
- FeeProportionalMillionths (Word32)
- HtlcMinimumMsat (Word64)
- HtlcMaximumMsat (Word64)

Each newtype includes accessor, NFData instance, and deriving
Eq/Ord/Show/Generic. Codec functions updated to wrap/unwrap,
validation updated for comparisons, tests updated to use
constructors.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

Diffstat:
Mlib/Lightning/Protocol/BOLT7/Codec.hs | 20++++++++++----------
Mlib/Lightning/Protocol/BOLT7/Types.hs | 36++++++++++++++++++++++++++----------
Mlib/Lightning/Protocol/BOLT7/Validate.hs | 2+-
Mtest/Main.hs | 78+++++++++++++++++++++++++++++++++++++++---------------------------------------
4 files changed, 76 insertions(+), 60 deletions(-)

diff --git a/lib/Lightning/Protocol/BOLT7/Codec.hs b/lib/Lightning/Protocol/BOLT7/Codec.hs @@ -372,13 +372,13 @@ encodeChannelUpdate msg = mconcat , Prim.encodeU32 (chanUpdateTimestamp msg) , BS.singleton (chanUpdateMsgFlags msg) , BS.singleton (chanUpdateChanFlags msg) - , Prim.encodeU16 (chanUpdateCltvExpDelta msg) - , Prim.encodeU64 (chanUpdateHtlcMinMsat msg) - , Prim.encodeU32 (chanUpdateFeeBaseMsat msg) - , Prim.encodeU32 (chanUpdateFeeProportional msg) + , Prim.encodeU16 (getCltvExpiryDelta (chanUpdateCltvExpDelta msg)) + , Prim.encodeU64 (getHtlcMinimumMsat (chanUpdateHtlcMinMsat msg)) + , Prim.encodeU32 (getFeeBaseMsat (chanUpdateFeeBaseMsat msg)) + , Prim.encodeU32 (getFeeProportionalMillionths (chanUpdateFeeProportional msg)) , case chanUpdateHtlcMaxMsat msg of Nothing -> BS.empty - Just m -> Prim.encodeU64 m + Just m -> Prim.encodeU64 (getHtlcMaximumMsat m) ] -- | Decode channel_update message. @@ -399,7 +399,7 @@ decodeChannelUpdate bs = do (htlcMax, rest) <- if msgFlags .&. 0x01 /= 0 then do (m, r) <- decodeU64 bs10 - Right (Just m, r) + Right (Just (HtlcMaximumMsat m), r) else Right (Nothing, bs10) let msg = ChannelUpdate { chanUpdateSignature = sig @@ -408,10 +408,10 @@ decodeChannelUpdate bs = do , chanUpdateTimestamp = timestamp , chanUpdateMsgFlags = msgFlags , chanUpdateChanFlags = chanFlags - , chanUpdateCltvExpDelta = cltvDelta - , chanUpdateHtlcMinMsat = htlcMin - , chanUpdateFeeBaseMsat = feeBase - , chanUpdateFeeProportional = feeProp + , chanUpdateCltvExpDelta = CltvExpiryDelta cltvDelta + , chanUpdateHtlcMinMsat = HtlcMinimumMsat htlcMin + , chanUpdateFeeBaseMsat = FeeBaseMsat feeBase + , chanUpdateFeeProportional = FeeProportionalMillionths feeProp , chanUpdateHtlcMaxMsat = htlcMax } Right (msg, rest) diff --git a/lib/Lightning/Protocol/BOLT7/Types.hs b/lib/Lightning/Protocol/BOLT7/Types.hs @@ -65,11 +65,11 @@ module Lightning.Protocol.BOLT7.Types ( , getTorV3Addr -- * Routing parameters - , CltvExpiryDelta - , FeeBaseMsat - , FeeProportionalMillionths - , HtlcMinimumMsat - , HtlcMaximumMsat + , CltvExpiryDelta(..) + , FeeBaseMsat(..) + , FeeProportionalMillionths(..) + , HtlcMinimumMsat(..) + , HtlcMaximumMsat(..) -- * Constants , chainHashLen @@ -404,16 +404,32 @@ instance NFData Address -- Routing parameters ---------------------------------------------------------- -- | CLTV expiry delta. -type CltvExpiryDelta = Word16 +newtype CltvExpiryDelta = CltvExpiryDelta { getCltvExpiryDelta :: Word16 } + deriving (Eq, Ord, Show, Generic) + +instance NFData CltvExpiryDelta -- | Base fee in millisatoshis. -type FeeBaseMsat = Word32 +newtype FeeBaseMsat = FeeBaseMsat { getFeeBaseMsat :: Word32 } + deriving (Eq, Ord, Show, Generic) + +instance NFData FeeBaseMsat -- | Proportional fee in millionths. -type FeeProportionalMillionths = Word32 +newtype FeeProportionalMillionths = FeeProportionalMillionths + { getFeeProportionalMillionths :: Word32 } + deriving (Eq, Ord, Show, Generic) + +instance NFData FeeProportionalMillionths -- | Minimum HTLC value in millisatoshis. -type HtlcMinimumMsat = Word64 +newtype HtlcMinimumMsat = HtlcMinimumMsat { getHtlcMinimumMsat :: Word64 } + deriving (Eq, Ord, Show, Generic) + +instance NFData HtlcMinimumMsat -- | Maximum HTLC value in millisatoshis. -type HtlcMaximumMsat = Word64 +newtype HtlcMaximumMsat = HtlcMaximumMsat { getHtlcMaximumMsat :: Word64 } + deriving (Eq, Ord, Show, Generic) + +instance NFData HtlcMaximumMsat diff --git a/lib/Lightning/Protocol/BOLT7/Validate.hs b/lib/Lightning/Protocol/BOLT7/Validate.hs @@ -90,7 +90,7 @@ validateChannelUpdate msg = do Nothing -> Right () Just htlcMax -> let htlcMin = chanUpdateHtlcMinMsat msg - in if htlcMin > htlcMax + in if getHtlcMinimumMsat htlcMin > getHtlcMaximumMsat htlcMax then Left ValidateHtlcAmounts else Right () diff --git a/test/Main.hs b/test/Main.hs @@ -203,10 +203,10 @@ channel_update_tests = testGroup "ChannelUpdate" [ , chanUpdateTimestamp = 1234567890 , chanUpdateMsgFlags = 0x00 , chanUpdateChanFlags = 0x01 - , chanUpdateCltvExpDelta = 144 - , chanUpdateHtlcMinMsat = 1000 - , chanUpdateFeeBaseMsat = 1000 - , chanUpdateFeeProportional = 100 + , chanUpdateCltvExpDelta = CltvExpiryDelta 144 + , chanUpdateHtlcMinMsat = HtlcMinimumMsat 1000 + , chanUpdateFeeBaseMsat = FeeBaseMsat 1000 + , chanUpdateFeeProportional = FeeProportionalMillionths 100 , chanUpdateHtlcMaxMsat = Nothing } encoded = encodeChannelUpdate msg @@ -221,11 +221,11 @@ channel_update_tests = testGroup "ChannelUpdate" [ , chanUpdateTimestamp = 1234567890 , chanUpdateMsgFlags = 0x01 -- bit 0 set , chanUpdateChanFlags = 0x00 - , chanUpdateCltvExpDelta = 40 - , chanUpdateHtlcMinMsat = 1000 - , chanUpdateFeeBaseMsat = 500 - , chanUpdateFeeProportional = 50 - , chanUpdateHtlcMaxMsat = Just 1000000000 + , chanUpdateCltvExpDelta = CltvExpiryDelta 40 + , chanUpdateHtlcMinMsat = HtlcMinimumMsat 1000 + , chanUpdateFeeBaseMsat = FeeBaseMsat 500 + , chanUpdateFeeProportional = FeeProportionalMillionths 50 + , chanUpdateHtlcMaxMsat = Just (HtlcMaximumMsat 1000000000) } encoded = encodeChannelUpdate msg case decodeChannelUpdate encoded of @@ -421,10 +421,10 @@ hash_tests = testGroup "Hash Functions" [ , chanUpdateTimestamp = 1234567890 , chanUpdateMsgFlags = 0x00 , chanUpdateChanFlags = 0x00 - , chanUpdateCltvExpDelta = 144 - , chanUpdateHtlcMinMsat = 1000 - , chanUpdateFeeBaseMsat = 1000 - , chanUpdateFeeProportional = 100 + , chanUpdateCltvExpDelta = CltvExpiryDelta 144 + , chanUpdateHtlcMinMsat = HtlcMinimumMsat 1000 + , chanUpdateFeeBaseMsat = FeeBaseMsat 1000 + , chanUpdateFeeProportional = FeeProportionalMillionths 100 , chanUpdateHtlcMaxMsat = Nothing } hashVal = channelUpdateHash msg @@ -440,10 +440,10 @@ hash_tests = testGroup "Hash Functions" [ , chanUpdateTimestamp = 1234567890 , chanUpdateMsgFlags = 0x00 , chanUpdateChanFlags = 0x00 - , chanUpdateCltvExpDelta = 144 - , chanUpdateHtlcMinMsat = 1000 - , chanUpdateFeeBaseMsat = 1000 - , chanUpdateFeeProportional = 100 + , chanUpdateCltvExpDelta = CltvExpiryDelta 144 + , chanUpdateHtlcMinMsat = HtlcMinimumMsat 1000 + , chanUpdateFeeBaseMsat = FeeBaseMsat 1000 + , chanUpdateFeeProportional = FeeProportionalMillionths 100 , chanUpdateHtlcMaxMsat = Nothing } cs1 = channelUpdateChecksum msg @@ -458,10 +458,10 @@ hash_tests = testGroup "Hash Functions" [ , chanUpdateTimestamp = 1000000000 , chanUpdateMsgFlags = 0x00 , chanUpdateChanFlags = 0x00 - , chanUpdateCltvExpDelta = 144 - , chanUpdateHtlcMinMsat = 1000 - , chanUpdateFeeBaseMsat = 1000 - , chanUpdateFeeProportional = 100 + , chanUpdateCltvExpDelta = CltvExpiryDelta 144 + , chanUpdateHtlcMinMsat = HtlcMinimumMsat 1000 + , chanUpdateFeeBaseMsat = FeeBaseMsat 1000 + , chanUpdateFeeProportional = FeeProportionalMillionths 100 , chanUpdateHtlcMaxMsat = Nothing } msg2 = encodeChannelUpdate ChannelUpdate @@ -471,10 +471,10 @@ hash_tests = testGroup "Hash Functions" [ , chanUpdateTimestamp = 2000000000 , chanUpdateMsgFlags = 0x00 , chanUpdateChanFlags = 0x00 - , chanUpdateCltvExpDelta = 144 - , chanUpdateHtlcMinMsat = 1000 - , chanUpdateFeeBaseMsat = 1000 - , chanUpdateFeeProportional = 100 + , chanUpdateCltvExpDelta = CltvExpiryDelta 144 + , chanUpdateHtlcMinMsat = HtlcMinimumMsat 1000 + , chanUpdateFeeBaseMsat = FeeBaseMsat 1000 + , chanUpdateFeeProportional = FeeProportionalMillionths 100 , chanUpdateHtlcMaxMsat = Nothing } channelUpdateChecksum msg1 @?= channelUpdateChecksum msg2 @@ -526,11 +526,11 @@ validation_tests = testGroup "Validation" [ , chanUpdateTimestamp = 1234567890 , chanUpdateMsgFlags = 0x01 , chanUpdateChanFlags = 0x00 - , chanUpdateCltvExpDelta = 144 - , chanUpdateHtlcMinMsat = 1000 - , chanUpdateFeeBaseMsat = 1000 - , chanUpdateFeeProportional = 100 - , chanUpdateHtlcMaxMsat = Just 1000000000 + , chanUpdateCltvExpDelta = CltvExpiryDelta 144 + , chanUpdateHtlcMinMsat = HtlcMinimumMsat 1000 + , chanUpdateFeeBaseMsat = FeeBaseMsat 1000 + , chanUpdateFeeProportional = FeeProportionalMillionths 100 + , chanUpdateHtlcMaxMsat = Just (HtlcMaximumMsat 1000000000) } validateChannelUpdate msg @?= Right () , testCase "rejects htlc_min > htlc_max" $ do @@ -541,11 +541,11 @@ validation_tests = testGroup "Validation" [ , chanUpdateTimestamp = 1234567890 , chanUpdateMsgFlags = 0x01 , chanUpdateChanFlags = 0x00 - , chanUpdateCltvExpDelta = 144 - , chanUpdateHtlcMinMsat = 2000000000 -- > htlcMax - , chanUpdateFeeBaseMsat = 1000 - , chanUpdateFeeProportional = 100 - , chanUpdateHtlcMaxMsat = Just 1000000000 + , chanUpdateCltvExpDelta = CltvExpiryDelta 144 + , chanUpdateHtlcMinMsat = HtlcMinimumMsat 2000000000 -- > htlcMax + , chanUpdateFeeBaseMsat = FeeBaseMsat 1000 + , chanUpdateFeeProportional = FeeProportionalMillionths 100 + , chanUpdateHtlcMaxMsat = Just (HtlcMaximumMsat 1000000000) } validateChannelUpdate msg @?= Left ValidateHtlcAmounts ] @@ -636,10 +636,10 @@ propChannelUpdateRoundtrip timestamp cltvDelta = property $ do , chanUpdateTimestamp = timestamp , chanUpdateMsgFlags = 0x00 , chanUpdateChanFlags = 0x00 - , chanUpdateCltvExpDelta = cltvDelta - , chanUpdateHtlcMinMsat = 1000 - , chanUpdateFeeBaseMsat = 1000 - , chanUpdateFeeProportional = 100 + , chanUpdateCltvExpDelta = CltvExpiryDelta cltvDelta + , chanUpdateHtlcMinMsat = HtlcMinimumMsat 1000 + , chanUpdateFeeBaseMsat = FeeBaseMsat 1000 + , chanUpdateFeeProportional = FeeProportionalMillionths 100 , chanUpdateHtlcMaxMsat = Nothing } encoded = encodeChannelUpdate msg