btc-lsp-0.1.0.0: src/BtcLsp/Thread/Server.hs
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeApplications #-}
module BtcLsp.Thread.Server
( apply,
)
where
import BtcLsp.Grpc.Data
import qualified BtcLsp.Grpc.Server.HighLevel as Server
import BtcLsp.Grpc.Server.LowLevel
import qualified BtcLsp.Grpc.Sig as Sig
import BtcLsp.Import
import qualified BtcLsp.Storage.Model.User as User
import qualified Crypto.Secp256k1 as C
import qualified Data.ByteString as BS
import Data.ProtoLens.Field
import Lens.Micro
import Network.GRPC.HTTP2.ProtoLens (RPC (..))
import Network.GRPC.Server
import qualified Network.Wai.Internal as Wai
import Proto.BtcLsp (Service)
import qualified Proto.BtcLsp.Data.HighLevel as Proto
import qualified Proto.BtcLsp.Data.HighLevel_Fields as Proto
import qualified Proto.BtcLsp.Method.SwapFromLn as SwapFromLn
import qualified Proto.BtcLsp.Method.SwapIntoLn as SwapIntoLn
import qualified Universum
apply :: (Env m) => m ()
apply = do
env <- getGsEnv
withUnliftIO $ runServer env . handlers
handlers ::
forall m.
( Env m
) =>
UnliftIO m ->
GSEnv ->
RawRequestBytes ->
[ServiceHandler]
handlers run gsEnv body =
[ unary (RPC :: RPC Service "swapIntoLn") $
runHandler Server.swapIntoLn,
unary (RPC :: RPC Service "swapFromLn") $
runHandler swapFromLn,
unary (RPC :: RPC Service "getCfg") $
runHandler Server.getCfg
]
where
runHandler ::
( GrpcReq req,
GrpcRes res failure specific,
Out req,
Out res
) =>
(Entity User -> req -> m res) ->
Wai.Request ->
req ->
IO res
runHandler =
withMiddleware run gsEnv body
verifySigE ::
GSEnv ->
Wai.Request ->
NodePubKey ->
RawRequestBytes ->
Either Failure ()
verifySigE env waiReq pubNode (RawRequestBytes payload) = do
pubDer <-
maybeToRight
( FailureInt . FailureGrpcServer $
"NodePubKey DER import failed from "
<> inspectPlain pubNode
)
. C.importPubKey
$ coerce pubNode
sig <-
Sig.sigToVerify (gsEnvSigHeaderName env) waiReq
msg <-
maybeToRight
( FailureInt . FailureGrpcServer $
"Incorrect message from "
<> inspectPlain payload
)
$ Sig.msgToVerify payload
if C.verifySig pubDer sig msg
then pure ()
else
Left
. FailureInt
. FailureGrpcServer
$ "Signature verification failed with key "
<> inspectPlain pubDer
<> " signature "
<> inspectPlain sig
<> " message "
<> inspectPlain payload
<> " of "
<> inspectPlain (BS.length payload)
<> " bytes and message hash "
<> inspectPlain (C.getMsg msg)
withMiddleware ::
( GrpcReq req,
GrpcRes res failure specific,
Env m,
Out req,
Out res
) =>
UnliftIO m ->
GSEnv ->
RawRequestBytes ->
(Entity User -> req -> m res) ->
Wai.Request ->
req ->
IO res
withMiddleware (UnliftIO run) gsEnv body handler waiReq req =
run $ do
userE <- runExceptT $ do
--
-- NOTE : Here request type is hardcoded,
-- because it's impossible to use generic "req"
-- type with TH. Hardcoding request type is not ideal,
-- because theoretically other requests could have
-- other location of context stuff,
-- but not really in this case.
--
nonce <-
fromReqT
$( mkFieldLocation
@SwapIntoLn.Request
[ "ctx",
"nonce"
]
)
$ req
^? field @"maybe'ctx"
. _Just
. Proto.maybe'nonce
. _Just
pub <-
fromReqT
$( mkFieldLocation
@SwapIntoLn.Request
[ "ctx",
"ln_pub_key"
]
)
$ req
^? field @"maybe'ctx"
. _Just
. Proto.maybe'lnPubKey
. _Just
if gsEnvSigVerify gsEnv
then
except
. first
( const $
newGenFailure
Proto.VERIFICATION_FAILED
mempty
)
$ verifySigE gsEnv waiReq pub body
else
$(logTM)
ErrorS
"WARNING!!! SIGNATURE VERIFICATION DISABLED!!!"
withExceptT
( const $
newGenFailure
Proto.VERIFICATION_FAILED
$( mkFieldLocation
@SwapIntoLn.Request
[ "ctx",
"nonce"
]
)
)
. ExceptT
. runSql
$ User.createVerifySql pub nonce
case userE of
Right user -> do
res <- setGrpcCtx =<< handler user req
$(logTM) DebugS . logStr $ debugMsg res
pure res
Left e -> do
$(logTM) DebugS . logStr $ debugMsg e
pure e
where
debugMsg :: (Out a) => a -> Text
debugMsg x =
"Got input "
<> inspect body
<> " with Wai request "
<> Universum.show waiReq
<> " and decoded "
<> inspect req
<> " producing result "
<> inspect x
swapFromLn ::
( Monad m
) =>
Entity User ->
SwapFromLn.Request ->
m SwapFromLn.Response
swapFromLn _ _ =
pure defMessage