btc-lsp-0.1.0.0: src/BtcLsp/Psbt/Utils.hs
module BtcLsp.Psbt.Utils
( swapUtxoToPsbtUtxo,
psbtShim,
fundPsbtReq,
openChannelReq,
psbtVerifyReq,
psbtFinalizeReq,
finalizePsbt,
unspendUtxoLookup,
shimCancelReq,
releaseUtxosPsbtLocks,
releaseUtxosLocks,
newLockId,
lockUtxo,
lockUtxos,
)
where
import BtcLsp.Import
import qualified Data.ByteString.Lazy as L
import qualified Data.Digest.Pure.SHA as SHA
( bytestringDigest,
sha256,
)
import qualified Data.Map as M
import qualified LndClient as Lnd
import qualified LndClient.Data.FinalizePsbt as FNP
import qualified LndClient.Data.FundPsbt as FP
import qualified LndClient.Data.FundingPsbtFinalize as FPF
import qualified LndClient.Data.FundingPsbtVerify as FSS
import qualified LndClient.Data.FundingShimCancel as FSC
import qualified LndClient.Data.FundingStateStep as FSS
import qualified LndClient.Data.LeaseOutput as LO
import qualified LndClient.Data.ListUnspent as LU
import qualified LndClient.Data.OpenChannel as Lnd
import qualified LndClient.Data.OutPoint as OP
import qualified LndClient.Data.PsbtShim as PS
import qualified LndClient.Data.ReleaseOutput as RO
import qualified LndClient.RPC.Katip as Lnd
import qualified Universum
swapUtxoToPsbtUtxo :: SwapUtxo -> PsbtUtxo
swapUtxoToPsbtUtxo x =
PsbtUtxo
( OP.OutPoint
(coerce $ swapUtxoTxid x)
(coerce $ swapUtxoVout x)
)
(coerce $ swapUtxoAmount x)
(swapUtxoLockId x)
psbtShim :: Lnd.PendingChannelId -> PS.PsbtShim
psbtShim pcid =
PS.PsbtShim
{ PS.pendingChanId = pcid,
PS.basePsbt = Nothing,
PS.noPublish = False
}
fundPsbtReq :: [OP.OutPoint] -> Map Text MSat -> FP.FundPsbtRequest
fundPsbtReq inputs outputs =
FP.FundPsbtRequest
{ FP.account = "",
FP.template = FP.TxTemplate inputs outputs,
FP.minConfs = 0,
FP.spendUnconfirmed = True,
FP.fee = FP.SatPerVbyte 1
}
openChannelReq ::
Lnd.PendingChannelId ->
Lnd.NodePubKey ->
Money 'Lsp 'Ln 'Gain ->
Money 'Usr 'Ln 'Gain ->
Privacy ->
Lnd.OpenChannelRequest
openChannelReq pcid toNode totalFundAmt pushAmt private =
Lnd.OpenChannelRequest
{ Lnd.nodePubkey = toNode,
Lnd.localFundingAmount = coerce totalFundAmt,
Lnd.pushMSat = Just $ coerce pushAmt,
Lnd.targetConf = Nothing,
Lnd.mSatPerByte = Nothing,
Lnd.private = Just $ private == Private,
Lnd.minHtlcMsat = Nothing,
Lnd.remoteCsvDelay = Nothing,
Lnd.minConfs = Nothing,
Lnd.spendUnconfirmed = Nothing,
Lnd.closeAddress = Nothing,
Lnd.fundingShim = Just (psbtShim pcid)
}
psbtVerifyReq :: Lnd.PendingChannelId -> Lnd.Psbt -> FSS.FundingStateStepRequest
psbtVerifyReq pcid fp =
FSS.FundingStateStepPsbtVerifyRequest $
FSS.FundingPsbtVerify
{ FSS.pendingChanId = pcid,
FSS.fundedPsbt = fp,
FSS.skipFinalize = False
}
psbtFinalizeReq :: Lnd.PendingChannelId -> Lnd.Psbt -> FSS.FundingStateStepRequest
psbtFinalizeReq pcid sp =
FSS.FundingStateStepPsbtFinalizeRequest $
FPF.FundingPsbtFinalize
{ FPF.signedPsbt = sp,
FPF.pendingChanId = pcid,
FPF.finalRawTx = Lnd.RawTx ""
}
shimCancelReq :: Lnd.PendingChannelId -> FSS.FundingStateStepRequest
shimCancelReq pcid =
FSS.FundingStateStepShimCancelRequest $
FSC.FundingShimCancel {FSC.pendingChanId = pcid}
finalizePsbt :: (Env m) => Lnd.Psbt -> ExceptT Failure m FNP.FinalizePsbtResponse
finalizePsbt psbt = withLndT Lnd.finalizePsbt ($ FNP.FinalizePsbtRequest (coerce psbt) "")
unspendUtxoLookup :: (Env m) => ExceptT Failure m (Map OP.OutPoint LU.Utxo)
unspendUtxoLookup = do
allUtxos <- LU.utxos <$> withLndT Lnd.listUnspent ($ LU.ListUnspentRequest 0 maxBound "")
pure $ foldr (\u acc -> M.insert (LU.outpoint u) u acc) M.empty allUtxos
releaseUtxosPsbtLocks ::
( Env m
) =>
[PsbtUtxo] ->
ExceptT Failure m ()
releaseUtxosPsbtLocks =
mapM_
( \refUtxo ->
whenJust
(getLockId refUtxo)
( \lid ->
void $
withLndT
Lnd.releaseOutput
( $
RO.ReleaseOutputRequest
(coerce lid)
(Just $ getOutPoint refUtxo)
)
)
)
releaseUtxosLocks ::
( Env m
) =>
[FP.UtxoLease] ->
ExceptT Failure m ()
releaseUtxosLocks =
mapM_ (\r -> withLndT Lnd.releaseOutput ($ toROR r))
where
toROR (FP.UtxoLease id' op _) =
RO.ReleaseOutputRequest id' (Just op)
newLockId :: OP.OutPoint -> UtxoLockId
newLockId u =
UtxoLockId
. L.toStrict
. SHA.bytestringDigest
. SHA.sha256
$ txid <> ":" <> vout
where
txid = L.fromStrict . coerce $ OP.txid u
vout = Universum.show $ OP.outputIndex u
lockUtxo :: (Env m) => OP.OutPoint -> ExceptT Failure m FP.UtxoLease
lockUtxo op = do
void $
withLndT
Lnd.leaseOutput
($ LO.LeaseOutputRequest (coerce lockId) (Just op) expS)
pure
FP.UtxoLease
{ FP.id = coerce lockId,
FP.expiration = expS,
FP.outpoint = op
}
where
expS :: Word64 = 3600 * 24 * 365 * 10
lockId = newLockId op
lockUtxos :: (Env m) => [OP.OutPoint] -> ExceptT Failure m [FP.UtxoLease]
lockUtxos = mapM lockUtxo