btc-lsp-0.1.0.0: src/BtcLsp/Storage/Model/LnChan.hs
{-# LANGUAGE TemplateHaskell #-}
module BtcLsp.Storage.Model.LnChan
( createUpdateSql,
getByChannelPointSql,
persistChannelUpdateSql,
persistOpenedChannelsSql,
persistClosedChannelsSql,
getBySwapIdSql,
getActiveNonSwapSql,
)
where
import BtcLsp.Import
import qualified BtcLsp.Import.Psql as Psql
import qualified BtcLsp.Storage.Model.SwapIntoLn as SwapIntoLn
import qualified BtcLsp.Storage.Util as Util
import qualified Data.Set as Set
import qualified LndClient.Data.Channel as Channel
import qualified LndClient.Data.Channel as Lnd
import qualified LndClient.Data.ChannelBackup as Lnd
import qualified LndClient.Data.ChannelPoint as ChannelPoint
import qualified LndClient.Data.ChannelPoint as Lnd
import qualified LndClient.Data.CloseChannel as CloseChannel
import qualified LndClient.Data.CloseChannel as Lnd
import qualified LndClient.Data.SubscribeChannelEvents as Lnd
createUpdateSql ::
( MonadIO m
) =>
SwapIntoLnId ->
TxId 'Funding ->
Vout 'Funding ->
ReaderT Psql.SqlBackend m (Entity LnChan)
createUpdateSql swapId txid vout = do
ct <- getCurrentTime
Psql.upsertBy
(UniqueLnChan txid vout)
LnChan
{ lnChanSwapIntoLnId = Just swapId,
lnChanFundingTxId = txid,
lnChanFundingVout = vout,
lnChanClosingTxId = Nothing,
lnChanExtId = Nothing,
lnChanBak = Nothing,
lnChanStatus = LnChanStatusPendingOpen,
lnChanInsertedAt = ct,
lnChanUpdatedAt = ct,
lnChanTransactedAt = ct,
lnChanTotalSatoshisReceived = MSat 0,
lnChanTotalSatoshisSent = MSat 0
}
[ LnChanSwapIntoLnId Psql.=. Psql.val (Just swapId),
LnChanUpdatedAt Psql.=. Psql.val ct
]
getByChannelPointSql ::
( Storage m
) =>
TxId 'Funding ->
Vout 'Funding ->
ReaderT Psql.SqlBackend m (Maybe (Entity LnChan))
getByChannelPointSql txid =
Util.lockByUnique
. UniqueLnChan txid
getBySwapIdSql ::
( Storage m
) =>
SwapIntoLnId ->
ReaderT Psql.SqlBackend m [Entity LnChan]
getBySwapIdSql swpId =
Psql.select $
Psql.from $ \c -> do
Psql.where_ (c Psql.^. LnChanSwapIntoLnId Psql.==. Psql.val (Just swpId))
pure c
getActiveNonSwapSql ::
( Storage m
) =>
ReaderT Psql.SqlBackend m [Entity LnChan]
getActiveNonSwapSql =
Psql.select $
Psql.from $ \row -> do
Psql.where_
( ( row Psql.^. LnChanStatus
Psql.==. Psql.val LnChanStatusActive
)
Psql.&&. Psql.isNothing
( row Psql.^. LnChanSwapIntoLnId
)
)
pure row
lazyUpdateSwapStatus ::
( MonadIO m
) =>
Entity LnChan ->
ReaderT Psql.SqlBackend m ()
lazyUpdateSwapStatus (Entity _ chanVal) = do
whenJust (lnChanSwapIntoLnId chanVal) $ \swapKey ->
when (lnChanStatus chanVal == LnChanStatusActive)
. void
. SwapIntoLn.withLockedRowSql swapKey (== SwapWaitingChan)
. const
$ SwapIntoLn.updateSucceededWithoutInvoiceSql swapKey
upsertChannelSql ::
( MonadIO m
) =>
UTCTime ->
Maybe LnChanStatus ->
Lnd.Channel ->
Maybe Lnd.SingleChanBackupBlob ->
ReaderT Psql.SqlBackend m (Entity LnChan)
upsertChannelSql ct mSS chan mBak =
maybeM
(upsert mempty)
(upsert . getOtherUpdates)
$ Util.lockByUnique uniq
where
upsert otherUpdates = do
chanEnt <-
Psql.upsertBy
uniq
LnChan
{ lnChanSwapIntoLnId = Nothing,
lnChanFundingTxId = txid,
lnChanFundingVout = vout,
lnChanClosingTxId = Nothing,
lnChanExtId = extId,
lnChanBak = mBak,
lnChanStatus = ss,
lnChanInsertedAt = ct,
lnChanUpdatedAt = ct,
lnChanTransactedAt = ct,
lnChanTotalSatoshisReceived = rcv,
lnChanTotalSatoshisSent = sent
}
$ [ LnChanExtId
Psql.=. Psql.val extId,
LnChanStatus
Psql.=. Psql.val ss,
LnChanUpdatedAt
Psql.=. Psql.val ct
]
<> maybe
mempty
(\x -> [LnChanBak Psql.=. Psql.val (Just x)])
mBak
<> otherUpdates
lazyUpdateSwapStatus chanEnt
pure chanEnt
getOtherUpdates (Entity _ x) =
if lnChanTotalSatoshisSent x == sent
&& lnChanTotalSatoshisReceived x == rcv
then mempty
else
[ LnChanTotalSatoshisSent
Psql.=. Psql.val sent,
LnChanTotalSatoshisReceived
Psql.=. Psql.val rcv,
LnChanTransactedAt
Psql.=. Psql.val ct
]
ss =
fromMaybe
( if Channel.active chan
then LnChanStatusActive
else LnChanStatusInactive
)
mSS
cp = Channel.channelPoint chan
txid = ChannelPoint.fundingTxId cp
vout = ChannelPoint.outputIndex cp
sent = Channel.totalSatoshisSent chan
rcv = Channel.totalSatoshisReceived chan
extId = Just $ Channel.chanId chan
uniq = UniqueLnChan txid vout
upsertChannelPointSql ::
( MonadIO m
) =>
UTCTime ->
LnChanStatus ->
Lnd.ChannelPoint ->
ReaderT Psql.SqlBackend m (Entity LnChan)
upsertChannelPointSql ct ss (Lnd.ChannelPoint txid vout) =
maybeM
upsert
(const upsert)
$ Util.lockByUnique uniq
where
uniq = UniqueLnChan txid vout
upsert = do
chanEnt <-
Psql.upsertBy
uniq
LnChan
{ lnChanSwapIntoLnId = Nothing,
lnChanFundingTxId = txid,
lnChanFundingVout = vout,
lnChanExtId = Nothing,
lnChanBak = Nothing,
lnChanClosingTxId = Nothing,
lnChanStatus = ss,
lnChanInsertedAt = ct,
lnChanUpdatedAt = ct,
lnChanTransactedAt = ct,
lnChanTotalSatoshisReceived = MSat 0,
lnChanTotalSatoshisSent = MSat 0
}
[ LnChanStatus
Psql.=. Psql.val ss,
LnChanUpdatedAt
Psql.=. Psql.val ct
]
lazyUpdateSwapStatus chanEnt
pure chanEnt
closedChannelUpsert ::
( MonadIO m
) =>
UTCTime ->
Lnd.ChannelCloseSummary ->
ReaderT Psql.SqlBackend m (Entity LnChan)
closedChannelUpsert ct close =
maybeM
upsert
(const upsert)
$ Util.lockByUnique uniq
where
upsert =
Psql.upsertBy
uniq
LnChan
{ lnChanSwapIntoLnId = Nothing,
lnChanFundingTxId = fundTxId,
lnChanFundingVout = fundVout,
lnChanClosingTxId = closeTxId,
lnChanExtId = extId,
lnChanBak = Nothing,
lnChanStatus = ss,
lnChanInsertedAt = ct,
lnChanUpdatedAt = ct,
lnChanTransactedAt = ct,
lnChanTotalSatoshisReceived = MSat 0,
lnChanTotalSatoshisSent = MSat 0
}
[ LnChanExtId
Psql.=. Psql.val extId,
LnChanClosingTxId
Psql.=. Psql.val closeTxId,
LnChanStatus
Psql.=. Psql.val ss,
LnChanUpdatedAt
Psql.=. Psql.val ct
]
ss = LnChanStatusClosed
cp = CloseChannel.chPoint close
fundTxId = ChannelPoint.fundingTxId cp
fundVout = ChannelPoint.outputIndex cp
closeTxId = Just $ CloseChannel.closingTxId close
extId = Just $ CloseChannel.chanId close
uniq = UniqueLnChan fundTxId fundVout
persistChannelUpdateSql ::
( KatipContext m
) =>
Lnd.ChannelEventUpdate ->
ReaderT Psql.SqlBackend m (Entity LnChan)
persistChannelUpdateSql (Lnd.ChannelEventUpdate channelEvent _) = do
$(logTM) DebugS . logStr $ inspect channelEvent
ct <- getCurrentTime
case channelEvent of
Lnd.ChannelEventUpdateChannelOpenChannel chan ->
upsertChannelSql ct (Just LnChanStatusOpened) chan Nothing
Lnd.ChannelEventUpdateChannelActiveChannel cp ->
upsertChannelPointSql ct LnChanStatusActive cp
Lnd.ChannelEventUpdateChannelInactiveChannel cp ->
upsertChannelPointSql ct LnChanStatusInactive cp
Lnd.ChannelEventUpdateChannelClosedChannel close ->
closedChannelUpsert ct close
Lnd.ChannelEventUpdateChannelFullyResolved cp ->
upsertChannelPointSql ct LnChanStatusFullyResolved cp
Lnd.ChannelEventUpdateChannelPendingOpenChannel
(Lnd.PendingUpdate txid vout) ->
upsertChannelPointSql ct LnChanStatusPendingOpen $
Lnd.ChannelPoint txid vout
persistOpenedChannelsSql ::
( MonadIO m
) =>
[(Lnd.Channel, Maybe Lnd.SingleChanBackupBlob)] ->
ReaderT Psql.SqlBackend m [Entity LnChan]
persistOpenedChannelsSql cs = do
ct <- getCurrentTime
forM (sortOn (Channel.channelPoint . fst) cs) $
uncurry (upsertChannelSql ct Nothing)
getNonClosedSql ::
( MonadIO m
) =>
ReaderT Psql.SqlBackend m [Entity LnChan]
getNonClosedSql =
Psql.select $
Psql.from $ \row -> do
Psql.where_
( row Psql.^. LnChanStatus
Psql.!=. Psql.val LnChanStatusClosed
)
pure row
persistClosedChannelsSql ::
( MonadIO m
) =>
[Lnd.ChannelCloseSummary] ->
ReaderT Psql.SqlBackend m [Entity LnChan]
persistClosedChannelsSql [] = pure mempty
persistClosedChannelsSql csRaw = do
ct <- getCurrentTime
nonClosedList <- getNonClosedSql
let nonClosedSet =
fromList $
( \(Entity {entityVal = x}) ->
Lnd.ChannelPoint
(lnChanFundingTxId x)
(lnChanFundingVout x)
)
<$> nonClosedList
let csUpd =
sortOn CloseChannel.chPoint $
filter
( (`Set.member` nonClosedSet)
. CloseChannel.chPoint
)
csRaw
forM csUpd $
closedChannelUpsert ct