bolt9

Lightning feature flags, per BOLT #9 (docs.ppad.tech/bolt9).
git clone git://git.ppad.tech/bolt9.git
Log | Files | Refs | README | LICENSE

commit f2fdc9afdc2b132e7e60ca6b4bef4b6e8902ceb4
parent 86b8dc8b59cecdb3147b5c4c2fe543cf7dd6767f
Author: Jared Tobin <jared@jtobin.io>
Date:   Sat, 10 Oct 2026 13:13:54 +0400

test: opposite-bit regression tests

Diffstat:
Mtest/Main.hs | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 57 insertions(+), 0 deletions(-)

diff --git a/test/Main.hs b/test/Main.hs @@ -27,6 +27,7 @@ tests = testGroup "ppad-bolt9" [ , validateNoBothBitsTests , remoteDependencyTests , longVectorTests + , oppositeBitTests , propertyTests ] @@ -478,6 +479,62 @@ longVectorTests = testGroup "Long vectors" [ Left [UnknownRequiredBit 524278] ] +-- Opposite bit tests ---------------------------------------------------------- + +-- | Look up a known feature by name, failing the test if absent. +knownFeature :: String -> IO Feature +knownFeature n = case featureByName n of + Nothing -> assertFailure ("feature not found: " ++ n) + Just f -> pure f + +oppositeBitTests :: TestTree +oppositeBitTests = testGroup "Opposite bit" [ + testCase "setFeature Optional clears the required bit" $ do + ps <- knownFeature "payment_secret" + let fv = setFeature ps Optional (setFeature ps Required empty) + setBits fv @?= [15] + hasFeature ps fv @?= Just Optional + + , testCase "setFeature Required clears the optional bit" $ do + ps <- knownFeature "payment_secret" + let fv = setFeature ps Required (setBit 15 empty) + setBits fv @?= [14] + hasFeature ps fv @?= Just Required + + , testCase "setFeatureWithDeps keeps a set dependency's level" $ do + ps <- knownFeature "payment_secret" + mpp <- knownFeature "basic_mpp" + let fv = setFeatureWithDeps mpp Optional + (setFeature ps Required empty) + hasFeature ps fv @?= Just Required + hasFeature mpp fv @?= Just Optional + validateLocal Init fv @?= Right () + + , testCase "setFeatureForContext clears the opposite bit" $ do + anc <- knownFeature "option_anchors" + let start = setFeature anc Required empty + case setFeatureForContext Init anc Optional start of + Left e -> assertFailure (show e) + Right fv -> do + hasFeature anc fv @?= Just Optional + validateNoBothBits fv @?= Right fv + + , testProperty "setFeature never sets both bits (any start)" $ + forAll genFeatureAndLevel $ \(f, lvl) bs -> + let fv = setFeature f lvl (parse (BS.pack bs)) + b = featureBaseBit f + in not (testBit b fv && testBit (b + 1) fv) + && hasFeature f fv == Just lvl + + , testProperty "setFeature leaves other bits alone" $ + forAll genFeatureAndLevel $ \(f, lvl) bs -> + let fv0 = parse (BS.pack bs) + fv = setFeature f lvl fv0 + b = fromIntegral (featureBaseBit f) + other = filter (\i -> i /= b && i /= b + 1) + in other (setBits fv) === other (setBits fv0) + ] + isInvalidParity :: ValidationError -> Bool isInvalidParity (InvalidParity _ _) = True isInvalidParity _ = False