packages feed

lnd-client-0.1.0.1: src/LndClient/RPC/TH.hs

{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE TemplateHaskell #-}

module LndClient.RPC.TH
  ( mkRpc,
    RpcKind (..),
  )
where

import Language.Haskell.TH.Syntax as TH
import LndClient.Data.AddHodlInvoice (AddHodlInvoiceRequest (..))
import LndClient.Data.AddInvoice (AddInvoiceRequest (..), AddInvoiceResponse (..))
import LndClient.Data.Channel (Channel (..))
import qualified LndClient.Data.ChannelBackup as Bak
import LndClient.Data.ChannelPoint (ChannelPoint (..))
import LndClient.Data.CloseChannel
  ( ChannelCloseSummary (..),
    CloseChannelRequest (..),
    CloseStatusUpdate (..),
  )
import LndClient.Data.ClosedChannels (ClosedChannelsRequest (..))
import LndClient.Data.FinalizePsbt
import LndClient.Data.FundPsbt (FundPsbtRequest, FundPsbtResponse)
import qualified LndClient.Data.FundingStateStep as FSS
import qualified LndClient.Data.GetInfo as GI
import LndClient.Data.HtlcEvent (HtlcEvent (..))
import qualified LndClient.Data.InitWallet as IW
import LndClient.Data.Invoice (Invoice (..))
import LndClient.Data.LeaseOutput (LeaseOutputRequest, LeaseOutputResponse)
import LndClient.Data.ListChannels (ListChannelsRequest (..))
import LndClient.Data.ListInvoices (ListInvoiceRequest (..), ListInvoiceResponse (..))
import LndClient.Data.ListLeases (ListLeasesRequest, ListLeasesResponse)
import LndClient.Data.ListUnspent
import LndClient.Data.NewAddress (NewAddressRequest (..), NewAddressResponse (..))
import LndClient.Data.OpenChannel (OpenChannelRequest (..), OpenStatusUpdate (..))
import LndClient.Data.PayReq (PayReq (..))
import LndClient.Data.Payment (Payment (..))
import LndClient.Data.Peer
  ( ConnectPeerRequest (..),
    LightningAddress (..),
    Peer (..),
  )
import LndClient.Data.PendingChannels (PendingChannelsResponse (..))
import LndClient.Data.PublishTransaction (PublishTransactionRequest, PublishTransactionResponse)
import LndClient.Data.ReleaseOutput (ReleaseOutputRequest, ReleaseOutputResponse)
import LndClient.Data.SendCoins (SendCoinsRequest, SendCoinsResponse)
import LndClient.Data.SendPayment (SendPaymentRequest (..), SendPaymentResponse (..))
import LndClient.Data.SignMessage
  ( SignMessageRequest (..),
    SignMessageResponse (..),
  )
import LndClient.Data.SubscribeChannelEvents (ChannelEventUpdate (..))
import LndClient.Data.SubscribeInvoices
  ( SubscribeInvoicesRequest (..),
  )
import LndClient.Data.TrackPayment (TrackPaymentRequest (..))
import qualified LndClient.Data.UnlockWallet as UW
import qualified LndClient.Data.VerifyMessage as VM
  ( VerifyMessageRequest (..),
    VerifyMessageResponse (..),
  )
import qualified LndClient.Data.WalletBalance as Wallet
import LndClient.Import
import LndClient.RPC.Generic
import Network.GRPC.HTTP2.ProtoLens (RPC (..))
import qualified Proto.Invoicesrpc.Invoices as LnGRPC
import qualified Proto.Lightning as LnGRPC
import qualified Proto.Lnrpc.Ln0 as LnGRPC
import qualified Proto.Lnrpc.Ln1 as LnGRPC
import qualified Proto.Routerrpc.Router as LnGRPC
import qualified Proto.Signrpc.Signer as LnGRPC
import qualified Proto.Walletrpc.Walletkit as LnGRPC
import qualified Proto.Walletunlocker as LnGRPC

data RpcKind = RpcSilent | RpcKatip

mkRpc :: RpcKind -> Q [Dec]
mkRpc k = do
  m <- VarT <$> newName "m"
  [d|
    getInfo ::
      $(tcc m) =>
      LndEnv ->
      $(pure m) (Either LndError GI.GetInfoResponse)
    getInfo env =
      $(grpcRetry) $
        $(grpcSync)
          (RPC :: RPC LnGRPC.Lightning "getInfo")
          env
          (defMessage :: LnGRPC.GetInfoRequest)

    initWallet ::
      $(tcc m) =>
      LndEnv ->
      $(pure m) (Either LndError ())
    initWallet env = do
      case envLndCipherSeedMnemonic env of
        Nothing -> pure . Left $ LndEnvError "CipherSeed is required for initWallet"
        Just seed -> do
          res <-
            $(grpcRetry) $
              $(grpcSync)
                (RPC :: RPC LnGRPC.WalletUnlocker "initWallet")
                env
                IW.InitWalletRequest
                  { IW.walletPassword =
                      coerce $ envLndWalletPassword env,
                    IW.cipherSeedMnemonic =
                      coerce seed,
                    IW.aezeedPassphrase =
                      coerce $ envLndAezeedPassphrase env
                  }
          if isRight res
            then waitForGrpc env
            else return res

    unlockWallet ::
      $(tcc m) =>
      LndEnv ->
      $(pure m) (Either LndError ())
    unlockWallet env = do
      res <-
        $(grpcRetry) $
          $(grpcSync)
            (RPC :: RPC LnGRPC.WalletUnlocker "unlockWallet")
            env
            UW.UnlockWalletRequest
              { UW.walletPassword = coerce $ envLndWalletPassword env,
                UW.recoveryWindow = 100
              }
      if isRight res
        then waitForGrpc env
        else return res

    newAddress ::
      $(tcc m) =>
      LndEnv ->
      NewAddressRequest ->
      $(pure m) (Either LndError NewAddressResponse)
    newAddress env =
      catchWalletLock env
        . $(grpcRetry)
        . $(grpcSync)
          (RPC :: RPC LnGRPC.Lightning "newAddress")
          env

    addInvoice ::
      $(tcc m) =>
      LndEnv ->
      AddInvoiceRequest ->
      $(pure m) (Either LndError AddInvoiceResponse)
    addInvoice env =
      catchWalletLock env
        . $(grpcRetry)
        . $(grpcSync)
          (RPC :: RPC LnGRPC.Lightning "addInvoice")
          env

    addHodlInvoice ::
      $(tcc m) =>
      LndEnv ->
      AddHodlInvoiceRequest ->
      $(pure m) (Either LndError PaymentRequest)
    addHodlInvoice env =
      catchWalletLock env
        . $(grpcRetry)
        . $(grpcSync)
          (RPC :: RPC LnGRPC.Invoices "addHoldInvoice")
          env

    cancelInvoice ::
      $(tcc m) =>
      LndEnv ->
      RHash ->
      $(pure m) (Either LndError ())
    cancelInvoice env =
      catchWalletLock env
        . $(grpcRetry)
        . $(grpcSync)
          (RPC :: RPC LnGRPC.Invoices "cancelInvoice")
          env

    settleInvoice ::
      $(tcc m) =>
      LndEnv ->
      RPreimage ->
      $(pure m) (Either LndError ())
    settleInvoice env =
      catchWalletLock env
        . $(grpcRetry)
        . $(grpcSync)
          (RPC :: RPC LnGRPC.Invoices "settleInvoice")
          env

    subscribeSingleInvoice ::
      $(tcc m) =>
      (Invoice -> IO ()) ->
      LndEnv ->
      RHash ->
      $(pure m) (Either LndError ())
    subscribeSingleInvoice handler env =
      catchWalletLock env
        . $(grpcSubscribe)
          (RPC :: RPC LnGRPC.Invoices "subscribeSingleInvoice")
          handler
          env

    subscribeSingleInvoiceChan ::
      $(tcc m) =>
      (req -> RHash) ->
      Maybe (TChan (req, Invoice)) ->
      LndEnv ->
      req ->
      $(pure m) (Either LndError ())
    subscribeSingleInvoiceChan accessor mq env req = do
      q <- fromMaybeM (atomically newBroadcastTChan) $ pure mq
      subscribeSingleInvoice
        (\x -> atomically $ writeTChan q (req, x))
        env
        $ accessor req

    subscribeInvoices ::
      $(tcc m) =>
      (Invoice -> IO ()) ->
      LndEnv ->
      SubscribeInvoicesRequest ->
      $(pure m) (Either LndError ())
    subscribeInvoices handler env =
      catchWalletLock env
        . $(grpcSubscribe)
          (RPC :: RPC LnGRPC.Lightning "subscribeInvoices")
          handler
          env

    subscribeInvoicesChan ::
      $(tcc m) =>
      Maybe (TChan (SubscribeInvoicesRequest, Invoice)) ->
      LndEnv ->
      SubscribeInvoicesRequest ->
      $(pure m) (Either LndError ())
    subscribeInvoicesChan mq env req = do
      q <- fromMaybeM (atomically newBroadcastTChan) $ pure mq
      subscribeInvoices
        (\x -> atomically $ writeTChan q (req, x))
        env
        req

    subscribeChannelEvents ::
      $(tcc m) =>
      (ChannelEventUpdate -> IO ()) ->
      LndEnv ->
      $(pure m) (Either LndError ())
    subscribeChannelEvents handler env =
      catchWalletLock env $
        $(grpcSubscribe)
          (RPC :: RPC LnGRPC.Lightning "subscribeChannelEvents")
          handler
          env
          (defMessage :: LnGRPC.ChannelEventSubscription)

    subscribeChannelEventsChan ::
      $(tcc m) =>
      Maybe (TChan ((), ChannelEventUpdate)) ->
      LndEnv ->
      $(pure m) (Either LndError ())
    subscribeChannelEventsChan mq env = do
      q <- fromMaybeM (atomically newBroadcastTChan) $ pure mq
      subscribeChannelEvents
        (\x -> atomically $ writeTChan q ((), x))
        env

    openChannel ::
      $(tcc m) =>
      (OpenStatusUpdate -> IO ()) ->
      LndEnv ->
      OpenChannelRequest ->
      $(pure m) (Either LndError ())
    openChannel handler env =
      catchWalletLock env
        . $(grpcSubscribe)
          (RPC :: RPC LnGRPC.Lightning "openChannel")
          handler
          env

    openChannelSync ::
      $(tcc m) =>
      LndEnv ->
      OpenChannelRequest ->
      $(pure m) (Either LndError ChannelPoint)
    openChannelSync env =
      catchWalletLock env
        . $(grpcSync) (RPC :: RPC LnGRPC.Lightning "openChannelSync") env

    listChannels ::
      $(tcc m) =>
      LndEnv ->
      ListChannelsRequest ->
      $(pure m) (Either LndError [Channel])
    listChannels env =
      catchWalletLock env
        . $(grpcRetry)
        . $(grpcSync)
          (RPC :: RPC LnGRPC.Lightning "listChannels")
          env

    listInvoices ::
      $(tcc m) =>
      LndEnv ->
      ListInvoiceRequest ->
      $(pure m) (Either LndError ListInvoiceResponse)
    listInvoices env =
      catchWalletLock env
        . $(grpcRetry)
        . $(grpcSync)
          (RPC :: RPC LnGRPC.Lightning "listInvoices")
          env

    closedChannels ::
      $(tcc m) =>
      LndEnv ->
      ClosedChannelsRequest ->
      $(pure m) (Either LndError [ChannelCloseSummary])
    closedChannels env =
      catchWalletLock env
        . $(grpcRetry)
        . $(grpcSync)
          (RPC :: RPC LnGRPC.Lightning "closedChannels")
          env

    closeChannel ::
      $(tcc m) =>
      (CloseStatusUpdate -> IO ()) ->
      LndEnv ->
      CloseChannelRequest ->
      $(pure m) (Either LndError ())
    closeChannel handler env =
      catchWalletLock env
        . $(grpcSubscribe)
          (RPC :: RPC LnGRPC.Lightning "closeChannel")
          handler
          env

    listPeers ::
      $(tcc m) =>
      LndEnv ->
      $(pure m) (Either LndError [Peer])
    listPeers env =
      catchWalletLock env
        . $(grpcRetry)
        $ $(grpcSync)
          (RPC :: RPC LnGRPC.Lightning "listPeers")
          env
          (defMessage :: LnGRPC.ListPeersRequest)

    connectPeer ::
      $(tcc m) =>
      LndEnv ->
      ConnectPeerRequest ->
      $(pure m) (Either LndError ())
    connectPeer env =
      catchWalletLock env
        . $(grpcRetry)
        . $(grpcSync)
          (RPC :: RPC LnGRPC.Lightning "connectPeer")
          env

    lazyConnectPeer ::
      $(tcc m) =>
      LndEnv ->
      ConnectPeerRequest ->
      $(pure m) (Either LndError ())
    lazyConnectPeer env cpr = do
      eps <- listPeers env
      case eps of
        Left e ->
          return $ Left e
        Right ps ->
          if any ((== pk) . pubKey) ps
            then return $ Right ()
            else connectPeer env cpr
      where
        pk = pubkey $ addr cpr

    sendPayment ::
      $(tcc m) =>
      LndEnv ->
      SendPaymentRequest ->
      $(pure m) (Either LndError SendPaymentResponse)
    sendPayment env =
      catchWalletLock env
        . $(grpcRetry)
        . $(grpcSync)
          (RPC :: RPC LnGRPC.Lightning "sendPaymentSync")
          env

    fundPsbt ::
      $(tcc m) =>
      LndEnv ->
      FundPsbtRequest ->
      $(pure m) (Either LndError FundPsbtResponse)
    fundPsbt env =
      catchWalletLock env
        . $(grpcRetry)
        . $(grpcSync)
          (RPC :: RPC LnGRPC.WalletKit "fundPsbt")
          env

    finalizePsbt ::
      $(tcc m) =>
      LndEnv ->
      FinalizePsbtRequest ->
      $(pure m) (Either LndError FinalizePsbtResponse)
    finalizePsbt env =
      catchWalletLock env
        . $(grpcRetry)
        . $(grpcSync)
          (RPC :: RPC LnGRPC.WalletKit "finalizePsbt")
          env

    publishTransaction ::
      $(tcc m) =>
      LndEnv ->
      PublishTransactionRequest ->
      $(pure m) (Either LndError PublishTransactionResponse)
    publishTransaction env =
      catchWalletLock env
        . $(grpcRetry)
        . $(grpcSync)
          (RPC :: RPC LnGRPC.WalletKit "publishTransaction")
          env

    listUnspent ::
      $(tcc m) =>
      LndEnv ->
      ListUnspentRequest ->
      $(pure m) (Either LndError ListUnspentResponse)
    listUnspent env =
      catchWalletLock env
        . $(grpcRetry)
        . $(grpcSync)
          (RPC :: RPC LnGRPC.WalletKit "listUnspent")
          env

    listLeases ::
      $(tcc m) =>
      LndEnv ->
      ListLeasesRequest ->
      $(pure m) (Either LndError ListLeasesResponse)
    listLeases env =
      catchWalletLock env
        . $(grpcRetry)
        . $(grpcSync)
          (RPC :: RPC LnGRPC.WalletKit "listLeases")
          env

    leaseOutput ::
      $(tcc m) =>
      LndEnv ->
      LeaseOutputRequest ->
      $(pure m) (Either LndError LeaseOutputResponse)
    leaseOutput env =
      catchWalletLock env
        . $(grpcRetry)
        . $(grpcSync)
          (RPC :: RPC LnGRPC.WalletKit "leaseOutput")
          env

    releaseOutput ::
      $(tcc m) =>
      LndEnv ->
      ReleaseOutputRequest ->
      $(pure m) (Either LndError ReleaseOutputResponse)
    releaseOutput env =
      catchWalletLock env
        . $(grpcRetry)
        . $(grpcSync)
          (RPC :: RPC LnGRPC.WalletKit "releaseOutput")
          env

    sendCoins ::
      $(tcc m) =>
      LndEnv ->
      SendCoinsRequest ->
      $(pure m) (Either LndError SendCoinsResponse)
    sendCoins env =
      $(grpcRetry)
        . $(grpcSync)
          (RPC :: RPC LnGRPC.Lightning "sendCoins")
          env

    subscribeHtlcEvents ::
      $(tcc m) =>
      (HtlcEvent -> IO ()) ->
      LndEnv ->
      $(pure m) (Either LndError ())
    subscribeHtlcEvents handler env =
      catchWalletLock env $
        $(grpcSubscribe)
          (RPC :: RPC LnGRPC.Router "subscribeHtlcEvents")
          handler
          env
          (defMessage :: LnGRPC.SubscribeHtlcEventsRequest)

    decodePayReq ::
      $(tcc m) =>
      LndEnv ->
      PaymentRequest ->
      $(pure m) (Either LndError PayReq)
    decodePayReq env =
      catchWalletLock env
        . $(grpcRetry)
        . $(grpcSync)
          (RPC :: RPC LnGRPC.Lightning "decodePayReq")
          env

    lookupInvoice ::
      $(tcc m) =>
      LndEnv ->
      RHash ->
      $(pure m) (Either LndError Invoice)
    lookupInvoice env =
      catchWalletLock env
        . $(grpcRetry)
        . $(grpcSync)
          (RPC :: RPC LnGRPC.Lightning "lookupInvoice")
          env

    trackPaymentV2 ::
      $(tcc m) =>
      (Payment -> IO ()) ->
      LndEnv ->
      TrackPaymentRequest ->
      $(pure m) (Either LndError ())
    trackPaymentV2 handler env =
      catchWalletLock env
        . $(grpcSubscribe)
          (RPC :: RPC LnGRPC.Router "trackPaymentV2")
          handler
          env

    trackPaymentV2Chan ::
      $(tcc m) =>
      Maybe (TChan (TrackPaymentRequest, Payment)) ->
      LndEnv ->
      TrackPaymentRequest ->
      $(pure m) (Either LndError ())
    trackPaymentV2Chan mc env req = do
      q <- fromMaybeM (atomically newBroadcastTChan) $ pure mc
      trackPaymentV2
        (\x -> atomically $ writeTChan q (req, x))
        env
        req

    pendingChannels ::
      $(tcc m) =>
      LndEnv ->
      $(pure m) (Either LndError PendingChannelsResponse)
    pendingChannels env =
      catchWalletLock env
        . $(grpcRetry)
        $ $(grpcSync)
          (RPC :: RPC LnGRPC.Lightning "pendingChannels")
          env
          (defMessage :: LnGRPC.PendingChannelsRequest)

    signMessage ::
      $(tcc m) =>
      LndEnv ->
      SignMessageRequest ->
      $(pure m) (Either LndError SignMessageResponse)
    signMessage env =
      catchWalletLock env
        . $(grpcRetry)
        . $(grpcSync)
          (RPC :: RPC LnGRPC.Signer "signMessage")
          env

    verifyMessage ::
      $(tcc m) =>
      LndEnv ->
      VM.VerifyMessageRequest ->
      $(pure m) (Either LndError VM.VerifyMessageResponse)
    verifyMessage env =
      catchWalletLock env
        . $(grpcRetry)
        . $(grpcSync)
          (RPC :: RPC LnGRPC.Signer "verifyMessage")
          env

    fundingStateStep ::
      $(tcc m) =>
      LndEnv ->
      FSS.FundingStateStepRequest ->
      $(pure m) (Either LndError ())
    fundingStateStep env =
      catchWalletLock env
        . $(grpcRetry)
        . $(grpcSync)
          (RPC :: RPC LnGRPC.Lightning "fundingStateStep")
          env

    exportAllChannelBackups ::
      $(tcc m) =>
      LndEnv ->
      $(pure m) (Either LndError [Bak.ChannelBackup])
    exportAllChannelBackups env =
      catchWalletLock env
        . $(grpcRetry)
        $ $(grpcSync)
          (RPC :: RPC LnGRPC.Lightning "exportAllChannelBackups")
          env
          (defMessage :: LnGRPC.ChanBackupExportRequest)

    exportChannelBackup ::
      $(tcc m) =>
      LndEnv ->
      ChannelPoint ->
      $(pure m) (Either LndError Bak.ChannelBackup)
    exportChannelBackup env =
      catchWalletLock env
        . $(grpcRetry)
        . $(grpcSync)
          (RPC :: RPC LnGRPC.Lightning "exportChannelBackup")
          env

    restoreChannelBackups ::
      $(tcc m) =>
      LndEnv ->
      [Bak.ChannelBackup] ->
      $(pure m) (Either LndError ())
    restoreChannelBackups env =
      catchWalletLock env
        . $(grpcRetry)
        . $(grpcSync)
          (RPC :: RPC LnGRPC.Lightning "restoreChannelBackups")
          env

    walletBalance ::
      $(tcc m) =>
      LndEnv ->
      $(pure m) (Either LndError Wallet.WalletBalance)
    walletBalance env =
      catchWalletLock env
        . $(grpcRetry)
        $ $(grpcSync)
          (RPC :: RPC LnGRPC.Lightning "walletBalance")
          env
          (defMessage :: LnGRPC.WalletBalanceRequest)
    |]
  where
    tcc :: TH.Type -> Q TH.Type
    tcc m = case k of
      RpcSilent ->
        [t|
          ( MonadUnliftIO $(pure m)
          )
          |]
      RpcKatip ->
        [t|
          ( KatipContext $(pure m),
            MonadUnliftIO $(pure m)
          )
          |]
    grpcRetry :: Q Exp
    grpcRetry = case k of
      RpcSilent -> [e|retrySilent|]
      RpcKatip -> [e|retryKatip|]
    grpcSync :: Q Exp
    grpcSync = case k of
      RpcSilent -> [e|grpcSyncSilent|]
      RpcKatip -> [e|grpcSyncKatip|]
    grpcSubscribe :: Q Exp
    grpcSubscribe = case k of
      RpcSilent -> [e|grpcSubscribeSilent|]
      RpcKatip -> [e|grpcSubscribeKatip|]