packages feed

btc-lsp-0.1.0.0: src/BtcLsp/Thread/Main.hs

{-# LANGUAGE TemplateHaskell #-}

module BtcLsp.Thread.Main
  ( main,
    apply,
    waitForSync,
  )
where

import BtcLsp.Data.AppM (runApp)
import qualified BtcLsp.Data.Env as Env
import BtcLsp.Import
import qualified BtcLsp.Storage.Migration as Storage
import qualified BtcLsp.Thread.BlockScanner as BlockScanner
import qualified BtcLsp.Thread.Expirer as Expirer
import qualified BtcLsp.Thread.LnChanOpener as LnChanOpener
import qualified BtcLsp.Thread.LnChanWatcher as LnChanWatcher
import qualified BtcLsp.Thread.Refunder as Refunder
import qualified BtcLsp.Thread.Server as Server
import qualified BtcLsp.Yesod.Application as Yesod
import Katip
import qualified LndClient.Data.GetInfo as Lnd
import qualified LndClient.RPC.Katip as Lnd
import qualified Network.Bitcoin.BlockChain as Btc

main :: IO ()
main = do
  startupScribe <-
    mkHandleScribe ColorIfTerminal stdout (permitItem InfoS) V2
  let startupLogEnv =
        registerScribe
          "stdout"
          startupScribe
          defaultScribeSettings
          =<< initLogEnv "BtcLsp" "startup"
  cfg <- bracket startupLogEnv closeScribes $ \le ->
    runKatipContextT le (mempty :: LogContexts) mempty $ do
      $(logTM) InfoS "Lsp is starting!"
      $(logTM) InfoS "Reading lsp raw environment..."
      cfg <- liftIO readRawConfig
      let secret title x =
            logStr $
              title
                <> " = "
                <> inspect
                  ( SecretLog (Env.rawConfigLogSecrets cfg) x
                  )
      let btc = Env.rawConfigBtcEnv cfg
      $(logTM) InfoS $
        secret
          "rawConfigBtcEnv"
          [ ("host" :: Text, Env.bitcoindEnvHost btc),
            ("user", Env.bitcoindEnvUsername btc),
            ("pass", Env.bitcoindEnvPassword btc)
          ]
      $(logTM) InfoS "Creating lsp runtime environment..."
      pure cfg
  withEnv cfg $
    \env -> runApp env apply

apply :: (Env m) => m ()
apply = do
  $(logTM) InfoS "Waiting for bitcoind..."
  waitForBitcoindSync
  $(logTM) InfoS "Waiting for lnd unlock..."
  unlocked <- withLnd Lnd.lazyUnlockWallet id
  if isRight unlocked
    then do
      $(logTM) InfoS "Waiting for lnd sync..."
      waitForLndSync
      $(logTM) InfoS "Running postgres migrations..."
      Storage.migrateAll
      log <- getYesodLog
      pool <- getSqlPool
      $(logTM) InfoS "Spawning lsp threads..."
      xs <-
        mapM
          spawnLink
          [ Server.apply,
            LnChanWatcher.applySub,
            LnChanWatcher.applyPoll,
            LnChanOpener.apply,
            BlockScanner.apply,
            Refunder.apply,
            Expirer.apply,
            withUnliftIO $ Yesod.appMain log pool
          ]
      $(logTM) InfoS "Lsp is running!"
      liftIO
        . void
        $ waitAnyCancel xs
    else
      $(logTM) ErrorS . logStr $
        "Can not unlock wallet, got "
          <> inspect unlocked
  $(logTM) ErrorS "Lsp terminates!"

waitForBitcoindSync :: (Env m) => m ()
waitForBitcoindSync =
  eitherM
    ( \e -> do
        $(logTM) ErrorS . logStr $ inspect e
        waitAndRetry
    )
    ( \x ->
        when (Btc.bciInitialBlockDownload x) $ do
          $(logTM) InfoS . logStr $ "Waiting IBD: " <> inspect x
          waitAndRetry
    )
    $ withBtc Btc.getBlockChainInfo id
  where
    waitAndRetry :: (Env m) => m ()
    waitAndRetry =
      sleep5s >> waitForBitcoindSync

waitForLndSync :: (Env m) => m ()
waitForLndSync =
  eitherM
    ( \e -> do
        $(logTM) ErrorS . logStr $ inspect e
        waitAndRetry
    )
    ( \x ->
        unless (Lnd.syncedToChain x) $ do
          $(logTM) InfoS . logStr $ "Waiting Lnd: " <> inspect x
          waitAndRetry
    )
    $ withLnd Lnd.getInfo id
  where
    waitAndRetry :: (Env m) => m ()
    waitAndRetry =
      sleep5s >> waitForLndSync

waitForSync :: (Env m) => m ()
waitForSync =
  waitForBitcoindSync
    >> waitForLndSync