packages feed

clplug-0.1.0.0: examples/routes/Main.hs

{-# LANGUAGE 
      DuplicateRecordFields
    , LambdaCase
    , DeriveGeneric
    , OverloadedStrings
    , BangPatterns
    , ViewPatterns
#-}
module Main where 

import Numeric 
import Data.Graph.Inductive.Graph
import Data.Graph.Inductive.PatriciaTree
import Data.Char 
import Control.Plugin
import Data.Lightning 
import Control.Client
import Data.Aeson
import Data.Aeson.Lens
import Control.Lens hiding ((.=))
import Control.Monad.IO.Class
import GHC.Generics  
import Data.Text (Text)
import Data.Lightning 
import Data.Lightning.Generic 
import Control.Client 
import Control.Conduit
import Control.Monad.State
import Control.Monad.Reader 
import Control.Applicative (liftA2) 
import Data.List 
import Data.Foldable 
import Route 
import Search 

type Gra = Gr () MyChan

main = plugin manifest start app

manifest = object [
      "dynamic" .= True
    , "options" .= ([] :: [Option])
    , "rpcmethods" .= [
         RpcMethod "route" "[n, m, a, l]" "l routes from n to m of a" Nothing False 
       , RpcMethod "network" "" "show metrics of loaded graph" Nothing False 
       ]
    ]

start :: PluginInit Gra
start (h, Init o c) = do
    Just (Res xd _) <- liftIO $ lightningCli h (Command "listnodes" nfilt noparams)
    Just (Res yd _) <- liftIO $ lightningCli h (Command "listchannels" cfilt noparams)
    case (fromJSON xd, fromJSON yd) :: (Result MyNodes, Result MyChans) of
        (Success nx, Success cx) -> pure $ mkGraph 
            (map toLNode (_nodes nx)) (map toLEdge' (channels cx))     
        _ -> pure empty
        where toLNode ni = ((getNodeInt.nodeid) ni , ())
              toLEdge' c = (
                      (getNodeInt.cSource) c
                    , (getNodeInt.cDest) c
                    , c
                    )

app (Just i, "route", v) =
    let n = getNodeInt <$> v ^? nth 0 
        m = getNodeInt <$> v ^? nth 1  
        a = maybe 100000000 fromInteger $ v ^? nth 2 . _Integer
        l = maybe 1 fromInteger $ v ^? nth 3 . _Integer
    in do
        g <- get
        case valid g n m of
            Just (n', m') -> do 
                r <- pure . map (createRoute a . toList) $ evalChans g n' m' l
                respond (object ["routes" .= r ]) i 
            _ -> respond (object ["invalid nodid" .= True]) i 
            where 

app (Just i, "network", _) = do 
    g <- get
    respond (object [
          "nodes" .= order g
        , "edges" .= size g
        , "capacity" .= capacity g 0
        ]) i 
        where capacity g t 
                  | isEmpty g = t  
                  | True = case matchAny g of 
                      (n, g') -> capacity g' $ t + (sum 
                          . map _amount_msat
                          . map snd  
                          . lsuc' $ n )

app (Just i, _, _) = release i
app _ = pure () 


valid g n m = case (n , m) of 
    (Just n', Just m') -> if gelem n' g && gelem m' g
                          then (Just (n', m'))
                          else Nothing   
    _ -> Nothing
noparams = object [] 
nfilt = object ["nodes" .= [nField]] 
nField = object ["nodeid" .= True] 

data MyNodes = M {
    _nodes :: [MyNode]  
    } deriving Generic 
instance FromJSON MyNodes where
    parseJSON = defaultParse
data MyNode = MyNode {
    nodeid :: Text
    } deriving Generic
instance FromJSON MyNode 

cfilt = object ["channels" .= [object [
      "source" .= True
    , "destination" .= True
    , "short_channel_id" .= True
    , "delay" .= True
    , "base_fee_millisatoshi" .= True       
    , "fee_per_millionth" .= True 
    , "amount_msat" .= True       
    ]]]

data MyChans = CC {
      channels :: [MyChan] 
    } deriving Generic
instance FromJSON MyChans
data MyChan = MyChan {
      _source :: Text
    , _destination :: Text
    , _short_channel_id :: Text
    , _delay :: Int
    , _base_fee_millisatoshi :: Msat
    , _fee_per_millionth :: Int 
    , _amount_msat :: Msat 
    } deriving Generic
instance FromJSON MyChan where 
    parseJSON = defaultParse
-- instance ToJSON MyChan
instance Channel MyChan where -- ? redundant?
    basefee = _base_fee_millisatoshi
    ppmrate = _fee_per_millionth
    cDelay = _delay 
    cDest = _destination
    cSource = _source
    shortid = _short_channel_id