{-# OPTIONS_HADDOCK prune #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DeriveGeneric #-}

-- |
-- Module: Lightning.Protocol.BOLT9.Features
-- Copyright: (c) 2025 Jared Tobin
-- License: MIT
-- Maintainer: Jared Tobin <jared@ppad.tech>
--
-- Known feature table for the Lightning Network, per
-- [BOLT #9](https://github.com/lightning/bolts/blob/master/09-features.md).

module Lightning.Protocol.BOLT9.Features (
    -- * Feature
    Feature(..)

    -- * Lookup
  , featureByBit
  , featureByName

    -- * Known features table
  , knownFeatures
  ) where

import Control.DeepSeq (NFData)
import Data.IntMap.Strict (IntMap)
import qualified Data.IntMap.Strict as IM
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as M
import Data.Word (Word16)
import GHC.Generics (Generic)
import Lightning.Protocol.BOLT9.Types (Context(..))

-- | A known feature from the BOLT #9 specification.
data Feature = Feature {
    Feature -> String
featureName         :: !String
    -- ^ The canonical name of the feature.
  , Feature -> Word16
featureBaseBit      :: {-# UNPACK #-} !Word16
    -- ^ The even (compulsory) bit number; the odd (optional) bit is
    --   @baseBit + 1@.
  , Feature -> [Context]
featureContexts     :: ![Context]
    -- ^ Contexts in which this feature may be presented.
  , Feature -> [String]
featureDependencies :: ![String]
    -- ^ Names of features this feature depends on.
  , Feature -> Bool
featureAssumed      :: !Bool
    -- ^ Whether this feature is assumed to be universally supported.
  }
  deriving (Feature -> Feature -> Bool
(Feature -> Feature -> Bool)
-> (Feature -> Feature -> Bool) -> Eq Feature
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Feature -> Feature -> Bool
== :: Feature -> Feature -> Bool
$c/= :: Feature -> Feature -> Bool
/= :: Feature -> Feature -> Bool
Eq, Key -> Feature -> ShowS
[Feature] -> ShowS
Feature -> String
(Key -> Feature -> ShowS)
-> (Feature -> String) -> ([Feature] -> ShowS) -> Show Feature
forall a.
(Key -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Key -> Feature -> ShowS
showsPrec :: Key -> Feature -> ShowS
$cshow :: Feature -> String
show :: Feature -> String
$cshowList :: [Feature] -> ShowS
showList :: [Feature] -> ShowS
Show, (forall x. Feature -> Rep Feature x)
-> (forall x. Rep Feature x -> Feature) -> Generic Feature
forall x. Rep Feature x -> Feature
forall x. Feature -> Rep Feature x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. Feature -> Rep Feature x
from :: forall x. Feature -> Rep Feature x
$cto :: forall x. Rep Feature x -> Feature
to :: forall x. Rep Feature x -> Feature
Generic)

instance NFData Feature

-- | The complete table of known features from BOLT #9.
knownFeatures :: [Feature]
knownFeatures :: [Feature]
knownFeatures = [
    String -> Word16 -> [Context] -> [String] -> Bool -> Feature
Feature String
"option_data_loss_protect" Word16
0 [] [] Bool
True
  , String -> Word16 -> [Context] -> [String] -> Bool -> Feature
Feature String
"option_upfront_shutdown_script" Word16
4 [Context
Init, Context
NodeAnn] [] Bool
False
  , String -> Word16 -> [Context] -> [String] -> Bool -> Feature
Feature String
"gossip_queries" Word16
6 [] [] Bool
False
  , String -> Word16 -> [Context] -> [String] -> Bool -> Feature
Feature String
"var_onion_optin" Word16
8 [] [] Bool
True
  , String -> Word16 -> [Context] -> [String] -> Bool -> Feature
Feature String
"gossip_queries_ex" Word16
10 [Context
Init, Context
NodeAnn] [] Bool
False
  , String -> Word16 -> [Context] -> [String] -> Bool -> Feature
Feature String
"option_static_remotekey" Word16
12 [] [] Bool
True
  , String -> Word16 -> [Context] -> [String] -> Bool -> Feature
Feature String
"payment_secret" Word16
14 [] [] Bool
True
  , String -> Word16 -> [Context] -> [String] -> Bool -> Feature
Feature String
"basic_mpp" Word16
16 [Context
Init, Context
NodeAnn, Context
Invoice]
      [String
"payment_secret"] Bool
False
  , String -> Word16 -> [Context] -> [String] -> Bool -> Feature
Feature String
"option_support_large_channel" Word16
18 [Context
Init, Context
NodeAnn] [] Bool
False
  , String -> Word16 -> [Context] -> [String] -> Bool -> Feature
Feature String
"option_anchors" Word16
22 [Context
Init, Context
NodeAnn, Context
ChanType] [] Bool
False
  , String -> Word16 -> [Context] -> [String] -> Bool -> Feature
Feature String
"option_route_blinding" Word16
24 [Context
Init, Context
NodeAnn, Context
Invoice] [] Bool
False
  , String -> Word16 -> [Context] -> [String] -> Bool -> Feature
Feature String
"option_shutdown_anysegwit" Word16
26 [Context
Init, Context
NodeAnn] [] Bool
False
  , String -> Word16 -> [Context] -> [String] -> Bool -> Feature
Feature String
"option_dual_fund" Word16
28 [Context
Init, Context
NodeAnn] [] Bool
False
  , String -> Word16 -> [Context] -> [String] -> Bool -> Feature
Feature String
"option_quiesce" Word16
34 [Context
Init, Context
NodeAnn] [] Bool
False
  , String -> Word16 -> [Context] -> [String] -> Bool -> Feature
Feature String
"option_attribution_data" Word16
36 [Context
Init, Context
NodeAnn, Context
Invoice]
      [] Bool
False
  , String -> Word16 -> [Context] -> [String] -> Bool -> Feature
Feature String
"option_onion_messages" Word16
38 [Context
Init, Context
NodeAnn] [] Bool
False
  , String -> Word16 -> [Context] -> [String] -> Bool -> Feature
Feature String
"option_provide_storage" Word16
42 [Context
Init, Context
NodeAnn] [] Bool
False
  , String -> Word16 -> [Context] -> [String] -> Bool -> Feature
Feature String
"option_channel_type" Word16
44 [] [] Bool
True
  , String -> Word16 -> [Context] -> [String] -> Bool -> Feature
Feature String
"option_scid_alias" Word16
46 [Context
Init, Context
NodeAnn, Context
ChanType] [] Bool
False
  , String -> Word16 -> [Context] -> [String] -> Bool -> Feature
Feature String
"option_payment_metadata" Word16
48 [Context
Invoice] [] Bool
False
  , String -> Word16 -> [Context] -> [String] -> Bool -> Feature
Feature String
"option_zeroconf" Word16
50 [Context
Init, Context
NodeAnn, Context
ChanType]
      [String
"option_scid_alias"] Bool
False
  , String -> Word16 -> [Context] -> [String] -> Bool -> Feature
Feature String
"option_simple_close" Word16
60 [Context
Init, Context
NodeAnn]
      [String
"option_shutdown_anysegwit"] Bool
False
  ]

-- | Look up a feature by bit number.
--
--   Accepts either the even (compulsory) or odd (optional) bit of the pair.
--
--   >>> fmap featureName (featureByBit 16)
--   Just "basic_mpp"
--   >>> fmap featureName (featureByBit 17)  -- odd bit also works
--   Just "basic_mpp"
--   >>> featureByBit 999
--   Nothing
featureByBit :: Word16 -> Maybe Feature
featureByBit :: Word16 -> Maybe Feature
featureByBit !Word16
bit =
  let !baseBit :: Key
baseBit = Word16 -> Key
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word16
bit Key -> Key -> Key
forall a. Num a => a -> a -> a
- (Word16 -> Key
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word16
bit Key -> Key -> Key
forall a. Integral a => a -> a -> a
`mod` Key
2)
  in  Key -> IntMap Feature -> Maybe Feature
forall a. Key -> IntMap a -> Maybe a
IM.lookup Key
baseBit IntMap Feature
featuresByBit
{-# INLINE featureByBit #-}

-- | Look up a feature by its canonical name.
--
--   >>> fmap featureBaseBit (featureByName "basic_mpp")
--   Just 16
--   >>> featureByName "nonexistent"
--   Nothing
featureByName :: String -> Maybe Feature
featureByName :: String -> Maybe Feature
featureByName !String
name = String -> Map String Feature -> Maybe Feature
forall k a. Ord k => k -> Map k a -> Maybe a
M.lookup String
name Map String Feature
featuresByName
{-# INLINE featureByName #-}

-- Lookup tables -------------------------------------------------------------

-- | Features indexed by base bit (even bit number).
featuresByBit :: IntMap Feature
featuresByBit :: IntMap Feature
featuresByBit = [(Key, Feature)] -> IntMap Feature
forall a. [(Key, a)] -> IntMap a
IM.fromList
  [(Word16 -> Key
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Feature -> Word16
featureBaseBit Feature
f), Feature
f) | Feature
f <- [Feature]
knownFeatures]

-- | Features indexed by canonical name.
featuresByName :: Map String Feature
featuresByName :: Map String Feature
featuresByName = [(String, Feature)] -> Map String Feature
forall k a. Ord k => [(k, a)] -> Map k a
M.fromList
  [(Feature -> String
featureName Feature
f, Feature
f) | Feature
f <- [Feature]
knownFeatures]